由于这篇文章似乎是搜索 RegEx Windows 路径验证模式的最佳结果之一,并且鉴于上述建议解决方案的注意事项/弱点,我'将包括我用于验证 Windows 路径的解决方案(我相信,它解决了之前在该用例中提出的所有问题)。
我无法想出一个可行的正则表达式,不管有没有前瞻和后视都可以完成这项工作,但我可以用两个来完成,没有任何前瞻或后瞻!
但请注意,连续的相对路径(即“..\..\folder\file.exe”)不会通过此模式(尽管在 处使用“..\”或“.\”字符串的开头)。斜杠前后或行尾的句点和空格,以及根据 Microsoft 的短文件名规范不允许的任何字符:
https://docs.microsoft.com/en-us/windows/win32/msi/filename
第一个模式:
^ (?# <- Start at the beginning of the line #)
(?# validate the opening drive or path delimiter, if present -> #)
(?: (?# "C:", "C:\", "C:..\", "C:.\" -> #)
(?:[A-Z]:(?:\.{1,2}[\/\\]|[\/\\])?)
| (?# or "\", "..\", ".\", "\\" -> #)
(?:[\/\\]{1,2}|\.{1,2}[\/\\])
)?
(?# validate the form and content of the body -> #)
(?:[^\x00-\x1A|*?\v\r\n\f+\/,;"'`\\:<>=[\]]+[\/\\]?)+
$ (?# <- End at the end of the line. #)
这通常会验证路径结构和字符有效性,但它也允许出现问题,例如双句点、双反斜杠,以及在空格或句点之前和/或后跟空格或句点的句点和反斜杠。也允许以空格和/或句点结尾的路径。
为了解决这些问题,我使用另一种(类似)模式进行了第二次测试:
^ (?# <- Start at the beginning of the line #)
(?# validate the opening drive or path delimiter, if present -> #)
(?: (?# "C:", "C:\", "C:..\", "C:.\" -> #)
(?:[A-Z]:(?:\.{1,2}[\/\\]|[\/\\])?)
| (?# or "\", "..\", ".\", "\\" -> #)
(?:[\/\\]{1,2}|\.{1,2}[\/\\])
)?
(?# ensure that undesired patterns aren't present in the string -> #)
(?:([^\/\\. ]|[^\/. \\][\/. \\][^\/. \\]|[\/\\]$)*
[^\x00-\x1A|*?\s+,;"'`:<.>=[\]]) (?# <- Ensure that the last character is valid #)
$ (?# <- End at the end of the line. #)
这验证了,在路径正文中,没有多个句点、多个斜杠、句号斜杠、空格斜杠、斜杠空格或斜杠句点出现,并且路径不以无效字符结尾.烦人的是,我必须重新验证 <root> 组,因为它是允许其中一些组合(即“.\”、“\\”和“..\”)的一个地方,我不想要那些使模式无效的。
这是我的测试的实现(在 C# 中):
/// <summary>Performs pattern testing on a string to see if it's in a form recognizable as an absolute path.</summary>
/// <param name="test">The string to test.</param>
/// <param name="testExists">If TRUE, this also verifies that the specified path exists.</param>
/// <returns>TRUE if the contents of the passed string are valid, and, if requested, the path exists.</returns>
public bool ValidatePath( string test, bool testExists = false )
{
bool result = !string.IsNullOrWhiteSpace(test);
string
drivePattern = /* language=regex */
@"^(([A-Z]:(?:\.{1,2}[\/\\]|[\/\\])?)|([\/\\]{1,2}|\.{1,2}[\/\\]))?",
pattern = drivePattern + /* language=regex */
@"([^\x00-\x1A|*?\t\v\f\r\n+\/,;""'`\\:<>=[\]]+[\/\\]?)+$";
result &= Regex.IsMatch( test, pattern, RegexOptions.ExplicitCapture );
pattern = drivePattern + /* language=regex */
@"(([^\/\\. ]|[^\/. \\][\/. \\][^\/. \\]|[\/\\]$)*[^\x00-\x1A|*?\s+,;""'`:<.>=[\]])$";
result &= Regex.IsMatch( test, pattern, RegexOptions.ExplicitCapture );
return result && (!testExists || Directory.Exists( test ));
}