Существует ли кроссплатформенный метод Java для удаления специальных символов имени файла? - PullRequest
52 голосов
/ 20 июля 2009

Я создаю кроссплатформенное приложение, которое переименовывает файлы на основе данных, полученных в Интернете. Я хотел бы очистить строки, которые я взял из веб-API для текущей платформы.

Я знаю, что разные платформы имеют разные требования к именам файлов, поэтому мне было интересно, есть ли кросс-платформенный способ сделать это?

Редактировать: На платформах Windows у вас не может быть знака вопроса '?' в имени файла, тогда как в Linux вы можете. Имена файлов могут содержать такие символы, и я хотел бы, чтобы платформы, поддерживающие эти символы, сохранили их, но в противном случае удалите их.

Кроме того, я бы предпочел стандартное решение Java, которое не требует сторонних библиотек.

Ответы [ 8 ]

25 голосов
/ 11 апреля 2011

Как предполагается в другом месте, это обычно не то, что вы хотите сделать. Обычно лучше создать временный файл, используя безопасный метод, такой как File.createTempFile ().

Вы не должны делать это с белым списком и хранить только «хорошие» символы. Если файл состоит только из китайских иероглифов, то вы удалите из него все. По этой причине мы не можем использовать белый список, мы должны использовать черный список.

Linux в значительной степени допускает все, что может быть настоящей болью. Я бы просто ограничил Linux тем же списком, что и Windows, чтобы избежать головной боли в будущем.

Используя этот фрагмент C # в Windows, я создал список символов, которые недопустимы в Windows. В этом списке гораздо больше символов, чем вы думаете (41), поэтому я бы не советовал пытаться создать свой собственный список.

        foreach (char c in new string(Path.GetInvalidFileNameChars()))
        {
            Console.Write((int)c);
            Console.Write(",");
        }

Вот простой Java-класс, который «очищает» имя файла.

public class FileNameCleaner {
final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47};
static {
    Arrays.sort(illegalChars);
}
public static String cleanFileName(String badFileName) {
    StringBuilder cleanName = new StringBuilder();
    for (int i = 0; i < badFileName.length(); i++) {
        int c = (int)badFileName.charAt(i);
        if (Arrays.binarySearch(illegalChars, c) < 0) {
            cleanName.append((char)c);
        }
    }
    return cleanName.toString();
}
}

EDIT: Как предположил Стивен, вам, вероятно, также следует убедиться, что эти обращения к файлам происходят только в разрешенном вами каталоге.

Следующий ответ содержит пример кода для создания пользовательского контекста безопасности в Java и последующего выполнения кода в этой «песочнице».

Как создать безопасную изолированную программную среду JEXL (для сценариев)?

19 голосов
/ 19 июля 2013

или просто сделайте это:

String filename = "A20/B22b#öA\\BC#Ä$%ld_ma.la.xps";
String sane = filename.replaceAll("[^a-zA-Z0-9\\._]+", "_");

Результат: A20_B22b_A_BC_ld_ma.la.xps

Пояснение:

[a-zA-Z0-9\\._] соответствует букве a-z в нижнем или верхнем регистре, цифр, точек и подчеркиваний

[^a-zA-Z0-9\\._] является обратным. все символы, которые не соответствуют первому выражению

[^a-zA-Z0-9\\._]+ - это последовательность символов, которые не соответствуют первому выражению

Таким образом, каждая последовательность символов, которая не состоит из символов от a-z, 0-9 или. _ будет заменено.

12 голосов
/ 17 октября 2014

Это основано на принятом ответе Сарел Бота , который прекрасно работает, если вы не встретите никаких символов за пределами Базовой многоязычной плоскости . Если вам нужна полная поддержка Юникода (а кому нет?), Используйте этот код, который безопасен для Юникода:

public class FileNameCleaner {
  final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47};

  static {
    Arrays.sort(illegalChars);
  }

  public static String cleanFileName(String badFileName) {
    StringBuilder cleanName = new StringBuilder();
    int len = badFileName.codePointCount(0, badFileName.length());
    for (int i=0; i<len; i++) {
      int c = badFileName.codePointAt(i);
      if (Arrays.binarySearch(illegalChars, c) < 0) {
        cleanName.appendCodePoint(c);
      }
    }
    return cleanName.toString();
  }
}

Ключевые изменения здесь:

  • Использовать codePointCount i.c.w. length вместо length
  • используйте codePointAt вместо charAt
  • используйте appendCodePoint вместо append
  • Нет необходимости приводить char с к int с. На самом деле, вы никогда не должны иметь дело с char s, поскольку они в основном сломаны для чего-либо за пределами BMP.
6 голосов
/ 11 июля 2014

Вот код, который я использую:

public static String sanitizeName( String name ) {
    if( null == name ) {
        return "";
    }

    if( SystemUtils.IS_OS_LINUX ) {
        return name.replaceAll( "/+", "" ).trim();
    }

    return name.replaceAll( "[\u0001-\u001f<>:\"/\\\\|?*\u007f]+", "" ).trim();
}

SystemUtils от Apache commons-lang3

6 голосов
/ 08 ноября 2012

Есть довольно хорошее встроенное Java-решение - Character.isXxx () .

Попробуйте Character.isJavaIdentifierPart(c):

String name = "name.é+!@#$%^&*(){}][/=?+-_\\|;:`~!'\",<>";
StringBuilder filename = new StringBuilder();

for (char c : name.toCharArray()) {
  if (c=='.' || Character.isJavaIdentifierPart(c)) {
    filename.append(c);
  }
}

Результат - "name.é $ _".

5 голосов
/ 21 июля 2009

Из вашего вопроса не ясно, но, поскольку вы планируете принимать имена путей из веб-формы (?), Вам, вероятно, следует блокировать попытки переименования определенных вещей;например, "C: \ Program Files".Это подразумевает, что вам нужно канонизировать имена путей, чтобы исключить "."и ".." перед проверкой доступа.

Учитывая это, я не буду пытаться удалить недопустимые символы.Вместо этого я бы использовал «new File (str) .getCanonicalFile ()» для создания канонических путей, затем проверил, чтобы они соответствовали вашим ограничениям в песочнице, и, наконец, использовал «File.exists ()», «File.isFile ()».и т. д. для проверки того, что источник и назначение являются кошерными и не являются одним и тем же объектом файловой системы.Я имел дело с недопустимыми символами, пытаясь выполнить операции и перехватывая исключения.

0 голосов
/ 06 февраля 2019

Paths.get(...) выдает подробное исключение с позицией недопустимого символа.

public static String removeInvalidChars(final String fileName)
{
  try
  {
    Paths.get(fileName);
    return fileName;
  }
  catch (final InvalidPathException e)
  {
    if (e.getInput() != null && e.getInput().length() > 0 && e.getIndex() >= 0)
    {
      final StringBuilder stringBuilder = new StringBuilder(e.getInput());
      stringBuilder.deleteCharAt(e.getIndex());
      return removeInvalidChars(stringBuilder.toString());
    }
    throw e;
  }
}
0 голосов
/ 24 февраля 2018

Если вы хотите использовать больше, чем как [A-Za-z0-9], тогда отметьте Соглашения об именах MS и не забудьте отфильтровать "... Символы, чьи целые представления находятся в диапазон от 1 до 31, ... ", как это делает пример Аарона Дигуллы. Код, например от Дэвида Карбони было бы недостаточно для этих персонажей.

Выдержка со списком зарезервированных символов:

Используйте любой символ в текущей кодовой странице для имени, включая символы Юникода и символы в расширенном наборе символов (128–255), за исключением следующего:

Следующие зарезервированные символы:

  • < (меньше)
  • > (больше чем)
  • : (двоеточие)
  • " (двойная кавычка)
  • / (косая черта)
  • \ (обратный слеш)
  • | (вертикальная черта или труба)
  • ? (знак вопроса)
  • * (звездочка)
  • Целочисленное значение ноль, иногда его называют символом ASCII NUL.
  • Символы, чьи целочисленные представления находятся в диапазоне от 1 до 31, за исключением альтернативных потоков данных, где эти символы разрешены. Для получения дополнительной информации о файловых потоках см. Файловые потоки.
  • Любой другой символ, который не разрешен целевой файловой системой.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...