在Windows上validation文件名

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

这种方法保证Windows上有效的文件名吗?

鉴于前面引用的MSDN文档中指定的要求,以下正则expression式应该做的很好:

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

请注意,这个正则expression式不会对文件名的长度施加任何限制,但根据平台的不同,实际的文件名可能被限制为260或32767个字符。

还不够,在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个字符。 但是由于完整的path(如C:\ Program Files \ filename.txt)包含在这个字符数中,所以文件名实际上比这个短。

这就是为什么在将具有非常长文件名的文件复制到比当前位置path更长的位置时,偶尔会遇到错误。

那么,我认为下面的方法将保证一个有效的文件名:

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

你怎么看?

一般来说,保证Windows文件名是有效的 – 确保创build一个名称的文件是合法的 – 是不可能实现的。

确保Windows文件名无效是相对直接的。 其他一些正则expression式试图做到这一点。 但是,原始问题要求更强大的断言:一种方法, 确保文件名在Windows上有效。

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

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

但是,即使在极端的情况下,还有其他的复杂性。 例如,名为“$ LogFile”的文件不能存在于NTFS卷的根目录中,但可以存在于卷的其他位置。 因此,不知道目录,我们不知道“$ LogFile”是否是一个有效的名字。 但是,如果“c:\ data \”是另一个NTFS卷根的符号链接,则甚至“C:\ data \ $ LogFile”也可能无效。 (同样,如果D:是NTFS卷的子目录的别名,则“D:\ $ LogFile”可以是有效的。

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

其他人提出了一个替代scheme:用build议的名称创build一个文件,然后删除它,当且仅当创build成功时返回true。 这种方法有几个实际和理论问题。 正如前面所指出的,有效性是文件名和path的函数,所以c:\ test \☃.txt的有效性可能与c:\ test2 \☃.txt的有效性不同。 另外,由于许多与文件有效性无关的原因,例如没有对目录的写入权限,该function将无法写入文件。 第三个缺陷是文件名的有效性不是非确定性的:例如假设的文件系统可能不允许被删除的文件被replace,或者(理论上)甚至可以随机地决定文件名是否有效。

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

发布一个新的答案,因为我没有代表评论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; } 

对您的答案进行小的更改,以防止删除预先存在的文件。 如果在这个方法调用期间创build的文件只被删除,而返回值是相同的。

在这里你可以find哪些文件名是允许的。

以下字符是不允许的:

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

  • :(冒号)
  • “(双引号)
  • /(正斜杠)
  • \(反斜杠)
  • | (竖条或pipe道)
  • ? (问号)
  • *(星号)

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

  • 整数表示在1到31范围内的字符,除了允许这些字符的交替数据stream。 有关文件stream的更多信息,请参阅文件stream。
  • 目标文件系统不允许的任何其他字符。

这个解决scheme只会根据操作系统规则检查给定的文件名是否有效,而不会创build文件。

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

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

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

这个正则expression式更简单,而且工作更快。

不知道如何在Java中实现它(正则expression式或自己的方法)。 但是,Windows操作系统有以下规则在文件系统中创build文件/目录:

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

所以,程序需要坚持这些规则。 希望它涵盖了你的问题的validation规则。

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

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

检查是否有其他限制。 但请注意,如果您在不存在的目录中检查名称,则无论名称是否真实有效,您都将收到ERROR_PATH_NOT_FOUND。

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

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

如何让File类做你的validation?

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