在Windows上验证文件名

时间:2011-07-18 07:51:45

标签: java regex windows string filenames

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

此方法是否保证Windows上的有效文件名?

11 个答案:

答案 0 :(得分:88)

考虑到之前cited MSDN documentation中指定的要求,以下正则表达式应该做得非常好:

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个字符,具体取决于平台。

答案 1 :(得分:26)

还不够,在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


编辑:

Windows通常会将文件名限制为 260 字符。但文件名实际上必须短于此,因为完整路径(例如C:\ Program Files \ filename.txt)包含在此字符数中。

这就是为什么在将文件名很长的文件复制到路径比当前位置长的位置时,偶尔会遇到错误。

答案 2 :(得分:16)

好吧,我认为以下方法可以保证有效的文件名:

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;
}

您怎么看?

答案 3 :(得分:13)

通常保证Windows文件名有效的方法 - 创建具有该名称的文件是合法的 - 将无法实现。

保证Windows文件名无效相对简单。一些其他正则表达式尝试这样做。但是,原始问题要求更强的断言:保证文件名的方法在Windows上有效。

其他答案中引用的MSDN reference表示Windows文件名不能包含“目标文件系统不允许的任何其他字符”。例如,包含NUL的文件在某些​​文件系统上将无效,在某些较旧的文件系统上也会扩展Unicode字符。因此,名为☃.txt的文件在某些​​情况下有效,但在其他情况下则无效。因此,假设的isValidName(\"☃\")是否返回true取决于底层文件系统。

但是,假设这样的函数是保守的,并且要求文件名由可打印的ASCII字符组成。所有现代版本的Windows本身都支持NTFS,FAT32和FAT16文件格式,这些格式接受Unicode文件名。但是可以安装任意文件系统的驱动程序,并且可以自由地创建不允许例如字母“n”的文件系统。因此,即使像“snowman.txt”这样的简单文件也不能“保证”有效。

但即使有极端情况,也有其他并发症。例如,名为“$ LogFile”的文件不能存在于NTFS卷的根目录中,但可以存在于卷的其他位置。因此,在不知道目录的情况下,我们无法知道“$ LogFile”是否是有效名称。但是,如果“c:\ data \”是另一个NTFS卷根的符号链接,即使“C:\ data \ $ LogFile”也可能无效。 (类似地,如果D:是NTFS卷的子目录的别名,则“D:\ $ LogFile”可以有效。)

还有更多的并发症。例如,文件上的备用数据流在NTFS卷上是合法的,因此“snowman.txt:☃”可能是有效的。所有三个主要Windows文件系统都有路径长度重构,因此文件名的有效性也是路径的函数。但是,如果路径是虚拟别名,映射网络驱动器或符号链接而不是卷上的物理路径,则isValidName可能无法使用物理路径的长度。

其他一些人建议了另一种方法:按建议的名称创建一个文件,然后将其删除,当且仅当创建成功时才返回true。这种方法存在一些实际和理论问题。如前所述,其中一个是有效性是文件名和路径的函数,因此c:\ test \☃.txt的有效性可能与c:\ test2 \☃.txt的有效性不同。此外,该函数将无法写入文件,原因与文件的有效性无关,例如没有对目录的写入权限。第三个缺陷是文件名的有效性不需要是不确定的:例如,假设的文件系统可能不允许替换已删除的文件,或者(理论上)甚至可以随机决定文件名是否有效。

作为替代方案,创建一个方法isInvalidFileName(String text)是相当简单的,如果文件保证在Windows中有效,则返回true;文件名如“aux”,“*”和“abc.txt”。会回归真实。文件创建操作首先检查文件名是否保证无效,如果返回false,则会停止。否则,该方法可能会尝试创建该文件,同时为边缘情况做准备,因为文件名无效而无法创建文件。

答案 4 :(得分:9)

发布新答案,因为我没有对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;
}

对您的答案进行细微更改,以防止删除预先存在的文件。如果文件在此方法调用期间创建,则只会删除文件,而返回值则相同。

答案 5 :(得分:7)

Here您可以找到允许的文件名。

不允许使用以下字符:

  • &LT; (小于)
  •   

    (大于)

  • :(冒号)
  • “(双引号)
  • /(正斜线)
  • \(反斜杠)
  • | (竖杆或竖管)
  • ? (问号)
  • *(星号)

  • 整数值为零,有时也称为ASCII NUL字符。

  • 整数表示形式在1到31范围内的字符,但允许使用这些字符的备用数据流除外。有关文件流的更多信息,请参阅文件流。
  • 目标文件系统不允许的任何其他字符。

答案 6 :(得分:6)

看起来不错。至少如果我们相信这个资源: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();
}

这个正则表达式更简单,工作更快。

答案 7 :(得分:6)

此解决方案仅根据操作系统规则检查给定文件名是否有效,而不创建文件。

实际创建文件时仍需要处理其他故障(例如权限不足,驱动器空间不足,安全限制)。

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
    }
  }

答案 8 :(得分:2)

不确定如何在Java中实现它(Regex或自己的方法)。但是,Windows OS具有以下规则来在文件系统中创建文件/目录:

  1. 名称不仅是Dots
  2. Windows设备名称,如AUX,CON,NUL,PRN,COM1,COM2,COM3, COM4,COM5,COM6,COM7,COM8,COM9,LPT1,LPT2,LPT3,LPT4,LPT5, LPT6,LPT7,LPT8,LPT9不能用于文件名,也不能用于 文件名的第一段(即test1.txt中的test1)。
  3. 设备名称不区分大小写。 (即prn,PRN,Prn等 相同。)
  4. 除“* /:&lt;&gt;?\ |
  5. 外,所有使用大于ASCII 31的字符

    因此,该计划需要遵守这些规则。希望,它涵盖了您问题的验证规则。

答案 9 :(得分:1)

您可以检查所有保留名称(AUX,CON等),然后使用此代码:

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

检查是否有任何其他限制。但请注意,如果您在非existant目录中检查名称,您将获得ERROR_PATH_NOT_FOUND,无论该名称是否真的有效。

无论如何,你应该记住那句老话:

  

要求宽恕比获得许可更容易。

答案 10 :(得分:-1)

让File类进行验证怎么样?

public static boolean isValidName(String text) {
    try {
        File file = new File(text);
        return file.getPath().equals(text);
    }
    catch(Exception ex){}
    return false;
}