Проверьте имя файла в Windows - PullRequest
55 голосов
/ 18 июля 2011
public static boolean isValidName(String text)
{
    Pattern pattern = Pattern.compile("^[^/./\\:*?\"<>|]+$");
    Matcher matcher = pattern.matcher(text);
    boolean isMatch = matcher.matches();
    return isMatch;
}

Гарантирует ли этот метод правильное имя файла в Windows?

Ответы [ 11 ]

88 голосов
/ 24 июля 2011

Учитывая требования, указанные в цитируемой документации MSDN , следующее регулярное выражение должно выполнять довольно хорошую работу:

public static boolean isValidName(String text)
{
    Pattern pattern = Pattern.compile(
        "# Match a valid Windows filename (unspecified file system).          \n" +
        "^                                # Anchor to start of string.        \n" +
        "(?!                              # Assert filename is not: CON, PRN, \n" +
        "  (?:                            # AUX, NUL, COM1, COM2, COM3, COM4, \n" +
        "    CON|PRN|AUX|NUL|             # COM5, COM6, COM7, COM8, COM9,     \n" +
        "    COM[1-9]|LPT[1-9]            # LPT1, LPT2, LPT3, LPT4, LPT5,     \n" +
        "  )                              # LPT6, LPT7, LPT8, and LPT9...     \n" +
        "  (?:\\.[^.]*)?                  # followed by optional extension    \n" +
        "  $                              # and end of string                 \n" +
        ")                                # End negative lookahead assertion. \n" +
        "[^<>:\"/\\\\|?*\\x00-\\x1F]*     # Zero or more valid filename chars.\n" +
        "[^<>:\"/\\\\|?*\\x00-\\x1F\\ .]  # Last char is not a space or dot.  \n" +
        "$                                # Anchor to end of string.            ", 
        Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.COMMENTS);
    Matcher matcher = pattern.matcher(text);
    boolean isMatch = matcher.matches();
    return isMatch;
}

Обратите внимание, что это регулярное выражение не накладывает никаких ограничений надлина имени файла, но реальное имя файла может быть ограничено 260 или 32767 символами в зависимости от платформы.

26 голосов
/ 18 июля 2011

Недостаточно, в Windows и DOS некоторые слова также могут быть зарезервированы и не могут использоваться в качестве имен файлов.

CON, PRN, AUX, CLOCK$, NUL
COM0, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9
LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.

См ~

http://en.wikipedia.org/wiki/Filename


Edit:

Windows обычно ограничивает имена файлов до 260 символов. Но имя файла должно быть короче, поскольку полный путь (например, C: \ Program Files \ filename.txt) включен в это количество символов.

Вот почему вы можете иногда сталкиваться с ошибкой при копировании файла с очень длинным именем файла в местоположение, которое имеет более длинный путь, чем его текущее местоположение.

16 голосов
/ 18 июля 2011

Ну, я думаю, следующий метод гарантирует правильное имя файла:

public static boolean isValidName(String text)
{
    try
    {
        File file = new File(text);
        file.createNewFile();
        if(file.exists()) file.delete();
        return true;
    }
    catch(Exception ex){}
    return false;
}

Как вы думаете?

13 голосов
/ 28 июля 2011

Метод, который гарантирует, как правило, допустимость имени файла Windows - что было бы законно создать файл с таким именем - было бы невозможно реализовать.

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

Ссылка MSDN , цитируемая в других ответах, указывает, что Windowsимя файла не может содержать «любой другой символ, который целевая файловая система не позволяет».Например, файл, содержащий NUL, будет недействительным в некоторых файловых системах, как и расширенные символы Unicode в некоторых старых файловых системах.Таким образом, файл с именем ☃.txt будет действительным в некоторых случаях, но не в других.То, будет ли гипотетический isValidName(\"☃\") возвращать true, зависит от базовой файловой системы.

Предположим, однако, что такая функция является консервативной и требует, чтобы имя файла состояло из печатных символов ASCII.Все современные версии Windows изначально поддерживают форматы файлов NTFS, FAT32 и FAT16, которые принимают имена файлов Unicode.Но драйверы для произвольных файловых систем могут быть установлены, и каждый может создать файловую систему, которая не допускает, например, буквы «n».Таким образом, даже простой файл, такой как «snowman.txt», не может быть «гарантированно» действительным.

Но даже за исключением крайних случаев, есть и другие сложности.Например, файл с именем «$ LogFile» не может существовать в корне тома NTFS, но может существовать в другом месте на томе.Таким образом, не зная каталога, мы не можем знать, является ли «$ LogFile» допустимым именем.Но даже «C: \ data \ $ LogFile» может быть недействительным, если, скажем, «c: \ data \» является символической ссылкой на другой корень тома NTFS.(Аналогично, «D: \ $ LogFile» может быть допустимым, если D: является псевдонимом подкаталога тома NTFS.)

Есть еще больше сложностей.Например, альтернативные потоки данных в файлах допустимы для томов NTFS, поэтому может быть допустимым «snowman.txt: ☃».Все три основные файловые системы Windows имеют восстановление длины пути, поэтому допустимость имени файла также является функцией пути.Но длина физического пути может быть даже недоступна для isValidName, если путь представляет собой виртуальный псевдоним, подключенный сетевой диск или символическую ссылку, а не физический путь на томе.

Некоторые другие предложилиальтернатива: создайте файл с предложенным именем, а затем удалите его, возвращая значение true, если и только если создание успешно завершено.Этот подход имеет несколько практических и теоретических проблем.Одна из них, как указывалось ранее, заключается в том, что валидность является функцией как имени файла, так и пути, поэтому валидность c: \ test \ ☃.txt может отличаться от валидности c: \ test2 \ ☃.txt.Кроме того, функция не сможет записать файл по ряду причин, не связанных с достоверностью файла, таких как отсутствие разрешения на запись в каталог.Третий недостаток заключается в том, что действительность имени файла не обязательно должна быть недетерминированной: гипотетическая файловая система может, например, не разрешать замену удаленного файла или (теоретически) может даже случайным образом решать, является ли имя файла допустимым.

В качестве альтернативы довольно просто создать метод isInvalidFileName(String text), который возвращает true, если для файла гарантировано, что не будет действительным в Windows;имена файлов, такие как "aux", "*" и "abc.txt."вернул бы истину.Операция создания файла сначала проверяет, что имя файла гарантированно является недействительным, и, если оно возвращает false, останавливается.В противном случае метод может попытаться создать файл, в то время как он готовится к граничному случаю, когда файл не может быть создан из-за неправильного имени файла.

9 голосов
/ 25 июля 2011

Отправка нового ответа, потому что у меня нет порога повторения, чтобы комментировать код Eng.Fouad

public static boolean isValidName(String text)
{
    try
    {
        File file = new File(text);
        if(file.createNewFile()) file.delete();
        return true;
    }
    catch(Exception ex){}
    return false;
}

Небольшое изменение в вашем ответе, которое предотвращает удаление существующего файла. Файлы удаляются только в том случае, если они были созданы во время этого вызова метода, а возвращаемое значение остается тем же.

7 голосов
/ 18 июля 2011

Здесь вы можете найти, какие имена файлов разрешены.

Следующие символы не допускаются:

  • <(меньше чем) </li>
  • (больше чем)

  • : (двоеточие)
  • "(двойная кавычка)
  • / (косая черта)
  • \ (обратная косая черта)
  • | (вертикальная черта или труба)
  • ? (вопросительный знак)
  • * (звездочка)

  • Целочисленное значение ноль, иногда его называют символом ASCII NUL.

  • Символы, чьи целочисленные представления находятся в диапазоне от 1 до 31, за исключением альтернативных потоков данных, где эти символы разрешены. Для получения дополнительной информации о файловых потоках см. Файловые потоки.
  • Любой другой символ, который не разрешен целевой файловой системой.
6 голосов
/ 17 августа 2012

Это решение будет только проверять, является ли данное имя файла допустимым в соответствии с правилами ОС, без создания файла.

Вам все еще нужно обрабатывать другие сбои при фактическом создании файла (например, недостаточные разрешения, нехватка места на диске, ограничения безопасности).

import java.io.File;
import java.io.IOException;

public class FileUtils {
  public static boolean isFilenameValid(String file) {
    File f = new File(file);
    try {
       f.getCanonicalPath();
       return true;
    }
    catch (IOException e) {
       return false;
    }
  }

  public static void main(String args[]) throws Exception {
    // true
    System.out.println(FileUtils.isFilenameValid("well.txt"));
    System.out.println(FileUtils.isFilenameValid("well well.txt"));
    System.out.println(FileUtils.isFilenameValid(""));

    //false
    System.out.println(FileUtils.isFilenameValid("test.T*T"));
    System.out.println(FileUtils.isFilenameValid("test|.TXT"));
    System.out.println(FileUtils.isFilenameValid("te?st.TXT"));
    System.out.println(FileUtils.isFilenameValid("con.TXT")); // windows
    System.out.println(FileUtils.isFilenameValid("prn.TXT")); // windows
    }
  }
6 голосов
/ 18 июля 2011

выглядит хорошо.По крайней мере, если мы поверим этому ресурсу: http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx

Но я бы упростил использование кода.Достаточно найти один из этих символов, чтобы сказать, что имя неверно, поэтому:

public static boolean isValidName(String text)
{
    Pattern pattern = Pattern.compile("[^/./\\:*?\"<>|]");
    return !pattern.matcher(text).find();
}

Это регулярное выражение проще и будет работать быстрее.

2 голосов
/ 18 августа 2011

Не уверен, как реализовать это в Java (Regex или собственный метод).Но ОС Windows имеет следующие правила для создания файла / каталога в файловой системе:

  1. Имя не только должно быть Точками
  2. Имена устройств Windows, такие как AUX, CON, NUL, PRNCOM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, нельзя использовать ни для имени файла, ни для первого сегментаимя файла (т. е. test1 в test1.txt).
  3. Имена устройств не чувствительны к регистру.(т. е. prn, PRN, Prn и т. д. идентичны.)
  4. Все символы, превышающие ASCII 31, которые должны использоваться, кроме "* /: <>? \ |

Итак,Программа должна придерживаться этих правил. Надеюсь, она охватывает правила проверки вашего вопроса.

1 голос
/ 28 июля 2011

Вы можете проверить все зарезервированные имена (AUX, CON и т. П.), А затем использовать этот код:

bool invalidName = GetFileAttributes(name) == INVALID_FILE_ATTRIBUTES && 
        GetLastError() == ERROR_INVALID_NAME;

, чтобы проверить наличие каких-либо дополнительных ограничений.Но обратите внимание, что если вы проверите имя в несуществующем каталоге, вы получите ERROR_PATH_NOT_FOUND независимо от того, действительно ли это имя действительно или нет.

В любом случае, вы должны запомнить старое изречение:

Проще просить прощения, чем получать разрешение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...