我试图为Javascript找到一个windows文件路径验证,但似乎没有一个能满足我想要的要求,所以我决定自己构建它。
要求如下:
- 路径不应为空
- 可以以x:\、x:\\、\、//开头,后跟文件名(无文件需要延期)
- 文件名不能包含以下特殊字符:<>:"|*
- 文件名不能以句点或空格结尾
以下是我提出的正则表达式:/^([a-z]:((\|/|\|//))|(\\|//)[^<>:"|?*]+/i
但也存在一些问题:
- 它还验证包含特殊字符的文件名规则中提到
- 它不包括最后一条规则(不能以:.或空格结尾)
var reg = new RegExp(/^([a-z]:((\|/|\\|//))|(\\|//))[^<>:"|?*]+/i);
var startList = [
'C://test',
'C://te?st.html',
'C:/test',
'C://test.html',
'C://test/hello.html',
'C:/test/hello.html',
'//test',
'/test',
'//test.html',
'//10.1.1.107',
'//10.1.1.107/test.html',
'//10.1.1.107/test/hello.html',
'//10.1.1.107/test/hello',
'//test/hello.txt',
'/test/html',
'/tes?t/html',
'/test.html',
'test.html',
'//',
'/',
'\\',
'\',
'/t!esrtr',
'C:/hel**o'
];
startList.forEach(item => {
document.write(reg.test(item) + ' >>> ' + item);
document.write("<br>");
});
不幸的是,正则表达式的JavaScript风格不支持lookbehinds,但幸运的是,它确实支持lookahead,这是关键因素如何构造正则表达式。
让我们从一些观察开始:
-
在点、斜杠、反斜杠或空格后面不能出现另一个点、斜线或反斜线。"禁用"字符集还包括
n
,因为这些字符都不能是文件名的最后一个字符或其片段(在点或(反)斜线之间)。 -
路径中允许的其他字符是您提到的字符(除了…),但"排除列表"还必须包括一个点,斜杠、反斜杠、空格和
n
(第1点中提到的字符)。 -
在"初始部分"(C:\)之后,可以有多个第1点或第2点提到的字符。
考虑到这些点,我从三个部分构建了正则表达式:
- "启动"部分,匹配驱动器号、冒号和最多2斜线(向前或向后)
- 第一种选择-点、斜线、反斜线或空格,带有负前瞻-在以上字符(见第1点)
- 第二种选择——第2点提到的字符
- 以上两种备选方案都可以出现多次(
+
量词)
因此正则表达式如下:
^
-字符串的开头(?:[a-z]:)?
-驱动器号和冒号,可选[/\]{0,2}
-反斜杠或斜线,介于0到2倍之间(?:
-非捕获组的启动,由于+
而需要后面的量词。[./\ ]
-第一个备选方案(?![./\n])
-否定前瞻-"禁止"字符
|
-或者。[^<>:"|?*./\ n]
-第二种选择
)+
-非捕获组结束,可能发生多次$
-字符串的末尾
如果尝试分别匹配每个路径,请仅使用i
选项。
但是,如果在单独的行中有多个路径,并将它们匹配一次性全局添加g
和m
选项。
有关工作示例,请参见https://regex101.com/r/4JY31I/1
注意:我认为!
也应该被视为禁用性格如果您同意,请将其添加到第二个备选方案中,例如*
之后。
这可能适用于您:^(?!.*[\/]s+)(?!(?:.*s|.*.|W+)$)(?:[a-zA-Z]:)?(?:(?:[^<>:"|?*n])+(?://|/|\\|\)?)+$
你在这里有一个演示
解释:
^
(?!.*[\/]s+) # Disallow files beginning with spaces
(?!(?:.*s|.*.|W+)$) # Disallow bars and finish with dot/space
(?:[a-zA-Z]:)? # Drive letter (optional)
(?:
(?:[^<>:"|?*n])+ # Word (non-allowed characters repeated one or more)
(?://|/|\\|\)? # Bars (// or / or \ or ); Optional
)+ # Repeated one or more
$
由于这篇文章似乎是搜索RegEx Windows路径验证模式的热门结果之一,并且考虑到上述提出的解决方案的注意事项/弱点,我将包括我用于验证Windows路径的解决方案(我相信,该解决方案解决了之前在该用例中提出的所有问题)。
我无法想出一个可行的REGEX,无论有没有look ahead和look behinds,都能完成这项工作,但我可以用两个来完成,没有任何look aheads或-behinds
不过,请注意,连续的相对路径(即"..\..\folder\file.exe")不会通过此模式(尽管在字符串的开头使用"..\"或".\")。斜杠前后或行尾的句点和空格,以及根据Microsoft的短文件名规范不允许使用的任何字符都将失败:https://learn.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|*?vrnf+/,;"'`\:<>=[]]+[/\]?)+
$ (?# <- 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>
组,因为它是允许其中一些组合的地方(即".\"、"\\"one_answers"..\"),我不希望这些组合使模式无效。
下面是我的测试(在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|*?tvfrn+/,;""'`\:<>=[]]+[/\]?)+$";
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 ));
}