Answers:
您可以从Path.GetInvalidPathChars
和获取无效字符列表GetInvalidFileNameChars
。
UPD:有关如何在正则表达式中使用它们的信息,请参阅Steve Cooper的建议。
UPD2:请注意,根据MSDN中的“备注”部分,“不能保证从此方法返回的数组包含文件和目录名称中无效的完整字符集。” sixlettervaliables提供的答案会更详细。
在MSDN的“命名文件或目录”中,以下是Windows下合法文件名的常规约定:
您可以在当前代码页中使用任何字符(Unicode / ANSI高于127),但以下情况除外:
<
>
:
"
/
\
|
?
*
一些可选的检查事项:
\?\
前缀)\?\
(请注意,前缀可能会扩展目录组件,并导致其超出32,000个限制)Regex unspupportedRegex = new Regex("(^(PRN|AUX|NUL|CON|COM[1-9]|LPT[1-9]|(\\.+)$)(\\..*)?$)|(([\\x00-\\x1f\\\\?*:\";|/<>])+)|(([\\. ]+)", RegexOptions.IgnoreCase);
^(?!^(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)(?:\.*?(?!\.))[^\x00-\x1f\\?*:\";|\/<>]+(?<![\s.])$
对于3.5之前的.Net Frameworks,它应该可以工作:
正则表达式匹配应该为您提供一些帮助。这是使用System.IO.Path.InvalidPathChars
常量的代码段;
bool IsValidFilename(string testName)
{
Regex containsABadCharacter = new Regex("["
+ Regex.Escape(System.IO.Path.InvalidPathChars) + "]");
if (containsABadCharacter.IsMatch(testName)) { return false; };
// other checks for UNC, drive-path format, etc
return true;
}
对于3.0之后的.Net Frameworks,它应该可以工作:
http://msdn.microsoft.com/zh-cn/library/system.io.path.getinvalidpathchars(v=vs.90).aspx
正则表达式匹配应该为您提供一些帮助。这是使用System.IO.Path.GetInvalidPathChars()
常量的代码段;
bool IsValidFilename(string testName)
{
Regex containsABadCharacter = new Regex("["
+ Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]");
if (containsABadCharacter.IsMatch(testName)) { return false; };
// other checks for UNC, drive-path format, etc
return true;
}
知道之后,您还应该检查其他格式,例如c:\my\drive
和\\server\share\dir\file.ext
尝试使用它,并捕获错误。允许的集合可能会在文件系统或Windows的不同版本中发生变化。换句话说,如果您想知道Windows是否喜欢该名称,请将该名称递给您,然后告诉您。
此类清除文件名和路径;像这样使用
var myCleanPath = PathSanitizer.SanitizeFilename(myBadPath, ' ');
这是代码;
/// <summary>
/// Cleans paths of invalid characters.
/// </summary>
public static class PathSanitizer
{
/// <summary>
/// The set of invalid filename characters, kept sorted for fast binary search
/// </summary>
private readonly static char[] invalidFilenameChars;
/// <summary>
/// The set of invalid path characters, kept sorted for fast binary search
/// </summary>
private readonly static char[] invalidPathChars;
static PathSanitizer()
{
// set up the two arrays -- sorted once for speed.
invalidFilenameChars = System.IO.Path.GetInvalidFileNameChars();
invalidPathChars = System.IO.Path.GetInvalidPathChars();
Array.Sort(invalidFilenameChars);
Array.Sort(invalidPathChars);
}
/// <summary>
/// Cleans a filename of invalid characters
/// </summary>
/// <param name="input">the string to clean</param>
/// <param name="errorChar">the character which replaces bad characters</param>
/// <returns></returns>
public static string SanitizeFilename(string input, char errorChar)
{
return Sanitize(input, invalidFilenameChars, errorChar);
}
/// <summary>
/// Cleans a path of invalid characters
/// </summary>
/// <param name="input">the string to clean</param>
/// <param name="errorChar">the character which replaces bad characters</param>
/// <returns></returns>
public static string SanitizePath(string input, char errorChar)
{
return Sanitize(input, invalidPathChars, errorChar);
}
/// <summary>
/// Cleans a string of invalid characters.
/// </summary>
/// <param name="input"></param>
/// <param name="invalidChars"></param>
/// <param name="errorChar"></param>
/// <returns></returns>
private static string Sanitize(string input, char[] invalidChars, char errorChar)
{
// null always sanitizes to null
if (input == null) { return null; }
StringBuilder result = new StringBuilder();
foreach (var characterToTest in input)
{
// we binary search for the character in the invalid set. This should be lightning fast.
if (Array.BinarySearch(invalidChars, characterToTest) >= 0)
{
// we found the character in the array of
result.Append(errorChar);
}
else
{
// the character was not found in invalid, so it is valid.
result.Append(characterToTest);
}
}
// we're done.
return result.ToString();
}
}
这是我用的:
public static bool IsValidFileName(this string expression, bool platformIndependent)
{
string sPattern = @"^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\"";|/]+$";
if (platformIndependent)
{
sPattern = @"^(([a-zA-Z]:|\\)\\)?(((\.)|(\.\.)|([^\\/:\*\?""\|<>\. ](([^\\/:\*\?""\|<>\. ])|([^\\/:\*\?""\|<>]*[^\\/:\*\?""\|<>\. ]))?))\\)*[^\\/:\*\?""\|<>\. ](([^\\/:\*\?""\|<>\. ])|([^\\/:\*\?""\|<>]*[^\\/:\*\?""\|<>\. ]))?$";
}
return (Regex.IsMatch(expression, sPattern, RegexOptions.CultureInvariant));
}
第一个模式创建一个正则表达式,仅包含Windows平台的无效/非法文件名和字符。第二个功能相同,但是确保名称对任何平台都是合法的。
@"^(?!(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)[^\x00-\x1F\xA5\\?*:\"";|\/<>]+(?<![\s.])$"
简化Eugene Katz的答案:
bool IsFileNameCorrect(string fileName){
return !fileName.Any(f=>Path.GetInvalidFileNameChars().Contains(f))
}
要么
bool IsFileNameCorrect(string fileName){
return fileName.All(f=>!Path.GetInvalidFileNameChars().Contains(f))
}
Path.GetInvalidFileNameChars
。在这里看看:referencesource.microsoft.com/#mscorlib/system/io/path.cs,289-为您的每个字符fileName
创建一个数组的克隆。
Microsoft Windows:Windows内核禁止使用范围为1-31的字符(即0x01-0x1F)和字符“ *:<>?\ |。尽管NTFS允许每个路径组件(目录或文件名)的长度为255个字符,并且Windows内核最多支持32767个字符的路径,Windows内核仅支持长达259个字符的路径。此外,Windows禁止使用MS-DOS设备名称AUX,CLOCK $,COM1,COM2,COM3,COM4,COM5,COM6, COM7,COM8,COM9,CON,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,NUL和PRN以及带有任何扩展名的这些名称(例如AUX.txt),除非使用长UNC路径(例如\。\ C:\ nul.txt或\?\ D:\ aux \ con)(实际上,如果提供了扩展名,则可以使用CLOCK $。)这些限制仅适用于Windows-例如,Linux允许使用“ *:<>?\ | 即使在NTFS中。
资料来源:http : //en.wikipedia.org/wiki/文件名
另外,CON,PRN,AUX,NUL,COM#和其他一些名称在任何扩展名的目录中都不是合法的文件名。
为了补充其他答案,以下是您可能要考虑的几个其他极端情况。
如果将工作簿保存在名称中包含'['或']'字符的文件中,Excel可能会出现问题。有关详细信息,请参见http://support.microsoft.com/kb/215205。
Sharepoint还有其他全部限制。有关详细信息,请参见http://support.microsoft.com/kb/905231。
这是一个已经回答的问题,但是仅出于“其他选择”的考虑,这是一个不理想的问题:
(不理想,因为使用异常作为流控制通常是“错误的事情”)
public static bool IsLegalFilename(string name)
{
try
{
var fileInfo = new FileInfo(name);
return true;
}
catch
{
return false;
}
}
true
。
如果您只是想检查包含文件名/路径的字符串中是否有任何无效字符,那么我发现的最快方法是使用Split()
该文件名将文件名分解成多个组成部分的数组,只要其中存在无效字符即可。如果结果仅是1的数组,则没有无效字符。:-)
var nameToTest = "Best file name \"ever\".txt";
bool isInvalidName = nameToTest.Split(System.IO.Path.GetInvalidFileNameChars()).Length > 1;
var pathToTest = "C:\\My Folder <secrets>\\";
bool isInvalidPath = pathToTest.Split(System.IO.Path.GetInvalidPathChars()).Length > 1;
我尝试在LinqPad中对文件/路径名称运行1,000,000次,并运行了上面提到的其他方法。
使用Split()
仅约850ms。
使用Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]")
大约需要6秒钟。
与其他一些选项一样,更复杂的正则表达式更糟,例如,使用Path
类上的各种方法来获取文件名并让其内部验证完成任务(很可能是由于异常处理的开销)。
当然,您很少需要验证一百万个文件名,因此对于大多数这些方法而言,一次迭代都可以。但是,如果您只查找无效字符,它仍然非常有效。
如果文件名太长且在Windows 10之前的环境中运行,这些答案中的许多将不起作用。同样,请考虑一下您要对句点进行的操作-允许前导或尾随在技术上是有效的,但是如果您不希望分别难以查看或删除文件,则会造成问题。
这是我创建的用于验证有效文件名的验证属性。
public class ValidFileNameAttribute : ValidationAttribute
{
public ValidFileNameAttribute()
{
RequireExtension = true;
ErrorMessage = "{0} is an Invalid Filename";
MaxLength = 255; //superseeded in modern windows environments
}
public override bool IsValid(object value)
{
//http://stackoverflow.com/questions/422090/in-c-sharp-check-that-filename-is-possibly-valid-not-that-it-exists
var fileName = (string)value;
if (string.IsNullOrEmpty(fileName)) { return true; }
if (fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 ||
(!AllowHidden && fileName[0] == '.') ||
fileName[fileName.Length - 1]== '.' ||
fileName.Length > MaxLength)
{
return false;
}
string extension = Path.GetExtension(fileName);
return (!RequireExtension || extension != string.Empty)
&& (ExtensionList==null || ExtensionList.Contains(extension));
}
private const string _sepChar = ",";
private IEnumerable<string> ExtensionList { get; set; }
public bool AllowHidden { get; set; }
public bool RequireExtension { get; set; }
public int MaxLength { get; set; }
public string AllowedExtensions {
get { return string.Join(_sepChar, ExtensionList); }
set {
if (string.IsNullOrEmpty(value))
{ ExtensionList = null; }
else {
ExtensionList = value.Split(new char[] { _sepChar[0] })
.Select(s => s[0] == '.' ? s : ('.' + s))
.ToList();
}
} }
public override bool RequiresValidationContext => false;
}
和测试
[TestMethod]
public void TestFilenameAttribute()
{
var rxa = new ValidFileNameAttribute();
Assert.IsFalse(rxa.IsValid("pptx."));
Assert.IsFalse(rxa.IsValid("pp.tx."));
Assert.IsFalse(rxa.IsValid("."));
Assert.IsFalse(rxa.IsValid(".pp.tx"));
Assert.IsFalse(rxa.IsValid(".pptx"));
Assert.IsFalse(rxa.IsValid("pptx"));
Assert.IsFalse(rxa.IsValid("a/abc.pptx"));
Assert.IsFalse(rxa.IsValid("a\\abc.pptx"));
Assert.IsFalse(rxa.IsValid("c:abc.pptx"));
Assert.IsFalse(rxa.IsValid("c<abc.pptx"));
Assert.IsTrue(rxa.IsValid("abc.pptx"));
rxa = new ValidFileNameAttribute { AllowedExtensions = ".pptx" };
Assert.IsFalse(rxa.IsValid("abc.docx"));
Assert.IsTrue(rxa.IsValid("abc.pptx"));
}
我的尝试:
using System.IO;
static class PathUtils
{
public static string IsValidFullPath([NotNull] string fullPath)
{
if (string.IsNullOrWhiteSpace(fullPath))
return "Path is null, empty or white space.";
bool pathContainsInvalidChars = fullPath.IndexOfAny(Path.GetInvalidPathChars()) != -1;
if (pathContainsInvalidChars)
return "Path contains invalid characters.";
string fileName = Path.GetFileName(fullPath);
if (fileName == "")
return "Path must contain a file name.";
bool fileNameContainsInvalidChars = fileName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1;
if (fileNameContainsInvalidChars)
return "File name contains invalid characters.";
if (!Path.IsPathRooted(fullPath))
return "The path must be absolute.";
return "";
}
}
这是不完美的,因为Path.GetInvalidPathChars
不会返回文件和目录名称中无效的完整字符集,当然还有很多细微之处。
因此,我使用此方法作为补充:
public static bool TestIfFileCanBeCreated([NotNull] string fullPath)
{
if (string.IsNullOrWhiteSpace(fullPath))
throw new ArgumentException("Value cannot be null or whitespace.", "fullPath");
string directoryName = Path.GetDirectoryName(fullPath);
if (directoryName != null) Directory.CreateDirectory(directoryName);
try
{
using (new FileStream(fullPath, FileMode.CreateNew)) { }
File.Delete(fullPath);
return true;
}
catch (IOException)
{
return false;
}
}
它尝试创建文件,如果有异常,则返回false。当然,我需要创建文件,但是我认为这是最安全的方法。另请注意,我不会删除已创建的目录。
您也可以使用第一种方法进行基本验证,然后在使用路径时仔细处理异常。
我建议只使用Path.GetFullPath()
string tagetFileFullNameToBeChecked;
try
{
Path.GetFullPath(tagetFileFullNameToBeChecked)
}
catch(AugumentException ex)
{
// invalid chars found
}
我是从某人那里得到这个想法的。-不知道是谁 让OS承担繁重的工作。
public bool IsPathFileNameGood(string fname)
{
bool rc = Constants.Fail;
try
{
this._stream = new StreamWriter(fname, true);
rc = Constants.Pass;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Problem opening file");
rc = Constants.Fail;
}
return rc;
}
这张支票
static bool IsValidFileName(string name)
{
return
!string.IsNullOrWhiteSpace(name) &&
name.IndexOfAny(Path.GetInvalidFileNameChars()) < 0 &&
!Path.GetFullPath(name).StartsWith(@"\\.\");
}
过滤掉无效字符(名称<>:"/\|?*
和ASCII 0-31),以及保留DOS设备(CON
,NUL
,COMx
)。它允许前导空格和全点名,与一致Path.GetFullPath
。(在系统上成功创建带有前导空格的文件)。
使用.NET Framework 4.7.1,已在Windows 7上进行了测试。
我认为,对此问题的唯一正确答案是尝试使用路径,并让OS和文件系统对其进行验证。否则,您将只是重新实现(并且可能效果很差)操作系统和文件系统已经使用的所有验证规则,并且如果将来更改了这些规则,则必须更改代码以使其匹配。
Windows文件名是没有限制的,因此实际上它甚至不是什么大问题。Windows禁止使用的字符是:
\ / : * ? " < > |
您可以轻松地编写表达式来检查这些字符是否存在。不过,更好的解决方案是尝试根据用户需要命名文件,并在文件名不固定时发出警告。