1. Обзор
В этом руководстве мы обсудим различные способы проверки того, имеет ли данная строка
допустимое имя файла для ОС, используя Java . Мы хотим проверить значение на соответствие ограниченным символам или ограничениям по длине.
В примерах мы просто сосредоточимся на основных решениях, не используя никаких внешних зависимостей. Мы проверим пакеты SDK java.io
и NIO2 и, наконец, реализуем собственные решения.
2. Использование java.io.File
Начнем с самого первого примера, используя класс java.io.File
. В этом решении нам нужно создать экземпляр File
с заданной строкой, а затем создать файл на локальном диске:
public static boolean validateStringFilenameUsingIO(String filename) throws IOException {
File file = new File(filename);
boolean created = false;
try {
created = file.createNewFile();
return created;
} finally {
if (created) {
file.delete();
}
}
}
Когда данное имя файла неверно, он генерирует исключение IOException
. Отметим, что из-за создания файла внутри этот метод требует, чтобы заданная строка
имени файла
не соответствовала уже существующему файлу. ``
Мы знаем, что разные файловые системы имеют свои собственные ограничения на имена файлов . Таким образом, используя методы java.io.File ,
нам не нужно указывать правила для каждой ОС , потому что Java автоматически позаботится об этом за нас.
Однако нам нужно создать фиктивный файл. Когда нам это удастся, мы должны не забыть удалить его в конце. ** ** Более того, мы должны убедиться, что у нас есть соответствующие разрешения для выполнения этих действий. Любые сбои также могут вызвать IOException
, поэтому также лучше проверить сообщение об ошибке:
// Windows invalid filename
assertThatThrownBy(() -> validateStringFilenameUsingIO("foreach?.txt"))
.isInstanceOf(IOException.class)
.hasMessageContaining("Invalid file path");
3. Использование API NIO2
Как мы знаем, у пакета java.io
много недостатков , потому что он создавался в первых версиях Java. API NIO2, преемник пакета java.io
, содержит множество улучшений, которые также значительно упрощают наше предыдущее решение:
public static boolean validateStringFilenameUsingNIO2(String filename) {
Paths.get(filename);
return true;
}
Теперь наша функция оптимизирована, так что это самый быстрый способ выполнить такой тест. Мы не создаем никаких файлов, поэтому нам не нужно иметь никаких прав доступа к диску и выполнять очистку после теста.
Недопустимое имя файла вызывает InvalidPathException ,
которое расширяет RuntimeException
. Сообщение об ошибке также содержит больше деталей , чем предыдущее:
// Windows invalid filename
assertThatThrownBy(() -> validateStringFilenameUsingNIO2(filename))
.isInstanceOf(InvalidPathException.class)
.hasMessageContaining("character not allowed");
У этого решения есть один серьезный недостаток, связанный с ограничениями файловой системы . Класс Path
может представлять путь к файлу с подкаталогами. В отличие от первого примера, этот метод не проверяет ограничение на переполнение имени файла. Давайте сравним его со случайной строкой
из пятисот символов , сгенерированной с помощью метода randomAlphabetic()
из Apache Commons:
String filename = RandomStringUtils.randomAlphabetic(500);
assertThatThrownBy(() -> validateStringFilenameUsingIO(filename))
.isInstanceOf(IOException.class)
.hasMessageContaining("File name too long");
assertThat(validateStringFilenameUsingNIO2(filename)).isTrue();
Чтобы исправить это, мы должны, как и раньше, создать файл и проверить результат.
4. Пользовательские реализации
Наконец, давайте попробуем реализовать нашу собственную пользовательскую функцию для проверки имен файлов. Мы также постараемся избежать каких-либо функций ввода-вывода и использовать только основные `` методы Java.
Такие решения дают больше контроля и позволяют нам применять собственные правила . Однако мы должны учитывать множество дополнительных ограничений для различных систем.
4.1. Использование String.contains
Мы можем использовать метод String.contains ()
, чтобы проверить, содержит ли данная строка
какой-либо из запрещенных символов. Прежде всего, нам нужно вручную указать некоторые примерные значения:
public static final Character[] INVALID_WINDOWS_SPECIFIC_CHARS = {'"', '*', ':', '<', '>', '?', '\\', '|', 0x7F};
public static final Character[] INVALID_UNIX_SPECIFIC_CHARS = {'\000'};
В нашем примере давайте сосредоточимся только на этих двух ОС. Как мы знаем, имена файлов в Windows более ограничены, чем в UNIX . Кроме того, некоторые пробельные символы могут быть проблематичными .
После определения ограниченных наборов символов давайте определим текущую ОС:
public static Character[] getInvalidCharsByOS() {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
return INVALID_WINDOWS_SPECIFIC_CHARS;
} else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) {
return INVALID_UNIX_SPECIFIC_CHARS;
} else {
return new Character[]{};
}
}
И теперь мы можем использовать его для проверки заданного значения:
public static boolean validateStringFilenameUsingContains(String filename) {
if (filename == null || filename.isEmpty() || filename.length() > 255) {
return false;
}
return Arrays.stream(getInvalidCharsByOS())
.noneMatch(ch -> filename.contains(ch.toString()));
}
Этот предикат Stream
возвращает значение true, если какой-либо из определенных нами символов отсутствует в заданном имени файла. Кроме того, мы реализовали поддержку нулевых
значений и неправильной длины.
4.2. Сопоставление шаблонов регулярных выражений
Мы также можем использовать регулярные выражения непосредственно для заданной строки
. Давайте реализуем шаблон, принимающий только буквенно-цифровые и точечные символы, длиной не более 255:
public static final String REGEX_PATTERN = "^[A-za-z0-9.]{1,255}$";
public static boolean validateStringFilenameUsingRegex(String filename) {
if (filename == null) {
return false;
}
return filename.matches(REGEX_PATTERN);
}
Теперь мы можем проверить заданное значение на заранее подготовленном шаблоне. Мы также можем легко изменить шаблон. В этом примере мы пропустили функцию проверки ОС.
5. Вывод
В этой статье мы сосредоточились на именах файлов и их ограничениях. Мы представили различные алгоритмы для обнаружения недопустимого имени файла с помощью Java.
Мы начали с пакета java.io
, который заботится о большинстве системных ограничений, но выполняет дополнительные действия ввода-вывода и может потребовать некоторых разрешений. Затем мы проверили NIO2 API, который является самым быстрым решением, с ограничением проверки длины имени файла .
Наконец, мы реализовали собственные методы, не используя API ввода-вывода, но требуя пользовательской реализации правил файловой системы .
Вы можете найти все примеры с дополнительными тестами на GitHub .