在Windows上验证文件名

Eng*_*uad 55 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;
}
Run Code Online (Sandbox Code Playgroud)

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

rid*_*ner 89

鉴于前面引用的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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,此正则表达式不会对文件名的长度施加任何限制,但实际文件名可能会限制为260或32767个字符,具体取决于平台.

  • 如果可能多次调用该模式,您也可以考虑仅编译一次. (12认同)

Mon*_*day 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.
Run Code Online (Sandbox Code Playgroud)

看看〜

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


编辑:

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

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

  • 所以模式是:`"^(?!(COM [0-9] | LPT [0-9] | CON | PRN | AUX | CLOCK\$ | NUL)$)[^./ \\:*?\"<> |] + $"` (3认同)

Eng*_*uad 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;
}
Run Code Online (Sandbox Code Playgroud)

你怎么看?

  • 此方法存在安全漏洞:如果已存在提交名称的文件,则会将其删除.由于没有采取任何措施来防止名称中的目录分隔符,因此系统上可写入调用进程的任何文件都可能容易受到此类攻击.提交者没有指定上下文,但我认为可能是Web应用程序服务器,因为这是大多数Java代码所做的事情...... (9认同)
  • +1我实际上喜欢它的简单性和100%保证.另外,关于权限,如果您没有权限,这种方法意味着*all*filenames对您无效,这仍然是正确的! (7认同)
  • 如果用户没有足够的权限创建文件怎么办? (5认同)
  • 我怀疑是否需要事先做好.如果您没有创建文件,为什么还需要验证文件名?=) (4认同)
  • 创建文件IO只是为了检查文件名的有效性,然后甚至不使用该文件? (2认同)

drf*_*drf 13

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

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

其他答案中引用的MSDN参考指示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)返回true,如果该文件是保证不会在Windows中是有效的; 文件名如"aux","*"和"abc.txt".会回归真实.文件创建操作首先检查文件名是否保证无效,如果返回false,则会停止.否则,该方法可能会尝试创建该文件,同时为边缘情况做好准备,因为文件名无效而无法创建文件.


Abd*_*uda 9

发布一个新的答案,因为我没有rep阈值来评论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;
}
Run Code Online (Sandbox Code Playgroud)

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


phi*_*mue 7

在这里,您可以找到允许的文件名.

不允许使用以下字符:

  • <(小于)
  • (比...更棒)

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

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

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

  • 由于目标文件系统可以施加额外的文件名限制,因此给定的正则表达式不能*保证Windows上的有效文件名. (3认同)

Ale*_*exR 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();
}
Run Code Online (Sandbox Code Playgroud)

这个正则表达式更简单,工作速度更快.


Rea*_*wTo 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
    }
  }
Run Code Online (Sandbox Code Playgroud)