我想验证用户的输入并仅将输入限制为字母数字字符(也可以允许使用下划线),但我不确定哪种方法最适合此。
我见过关于SA的各种例子,第一个为我提出一些问题的例子如下:
:input
set "in="
set /p "in=Please enter your username: "
ECHO(%in%|FINDSTR /ri "^[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ][0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ]*$" >nul || (
goto input
)
我看到第二个案例与第一个案例相同(作为期望,领先^
和结束*$
)。
为什么在以下情况也适用的情况下需要额外的案例和^
*$
?:
:input
set "in="
set /p "in=Please enter your username: "
ECHO(%in%|FINDSTR /ri "[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ]" >nul || (
goto input
)
最后,我在这里注意到的FOR /F
循环方法:
for /f "delims=1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ" %%a in ("%in%") do goto :input
在前面提到的FINDSTR正则表达式中使用它有没有(dis)优势?
答案 0 :(得分:2)
首先,您必须使用延迟扩展来引用环境变量in
,以避免因用户输入包含><|&"
等关键字符的字符串时出现语法错误而导致批处理文件执行退出。始终考虑在执行命令行之前扩展用%variable%
指定的变量,这很容易破坏用户输入变量字符串的批处理执行。
其次,强烈建议在提示后立即验证用户是否输入了任何内容,即在提示命令行后使用if not defined in goto input
。
第三,我认为 FOR 方法因为速度更快而更好。
FINDSTR 不是cmd.exe
的内部命令,例如 FOR 。因此,当在没有路径且没有文件扩展名的批处理文件中指定 FINDSTR 时,Windows命令解释程序必须首先搜索此可执行文件,并希望通过%SystemRoot%\System32\findstr.exe
和PATHEXT
真正找到PATH
接下来,在后台运行防病毒进程时,findstr.exe
的执行会触发反病毒进程的扫描过程,从而导致执行延迟。
Windows命令解释程序执行像 FINDSTR 这样的应用程序,即使没有运行防病毒扫描进程,执行内部命令cmd.exe
也会花费更长的时间。因此 FOR 循环方法最有可能(未经我验证)比 FINDSTR 方法更快。
使用 FINDSTR 时,需要使用正则表达式字符^
和*$
,因为正则表达式搜索字符串[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ]
会在处理后的行中产生正匹配包含至少1位数字或字母。因此,不检查行(=变量的字符串)是否仅包含数字和字母。取决于选项[0-9A-Z]
或/I
的较短字符类定义[0-9A-Za-z]
在这种情况下无法使用,正如aschipfl在下面的评论中所解释的那样。
指定^
必须在行的开头找到搜索到的字符串,*
必须找到0个或更多个数字或字母,$
表示搜索字符串必须在行尾找到。或者换句话说,之前检查过的整行(用户输入)不是完全为空必须完全由正数匹配的数字和字母组成。
对于每个内部或外部命令,可以通过在/?
作为参数的命令提示符窗口中运行命令来获取help命令。尝试打开命令提示符窗口并运行findstr /?
和for /?
以及set /?
。
答案 1 :(得分:2)
为了安全验证用户输入,两种方法都是可靠的,但您必须改进它们:
findstr
方法首先,让我们关注像^[...][...]*$
这样的搜索字符串(其中...
代表一个字符类,意思是一组字符):一个字符类[...]
匹配任何一个集...
中的字符; *
表示重复,因此匹配零个或多个匹配项,因此[...]*
匹配集合...
中出现的零个或多个字符;因此,[...][...]*
匹配来自集...
的一个或多个字符。前导^
将匹配锚定到行的开头,尾随$
将其锚定到结尾;因此,当指定两个锚点时,整行必须与搜索字符串匹配。
关于字符类[...]
:根据线程What are the undocumented features and limitations of the Windows FINDSTR command?,类是错误的;例如,班级[A-Z]
匹配小写字母b
与z
,[a-z]
匹配大写字母A
与Y
(当然这在不区分大小写的搜索的情况下无关紧要,所以当给出/I
时;类[0-9]
可能会匹配²
或³
,具体取决于当前的代码页; [A-Z]
和[a-z]
可能会匹配Á
或á
等特殊字母,例如,也取决于当前的代码页。因此,为了安全地匹配某些字符,请不要使用范围,而是单独指定每个字符,例如[0123456789]
,[ABCDEFGHIJKLMNOPQRSTUVWXYZ]
或[abcdefghijklmnopqrstuvwxyz]
。
所有这些都引导我们进入以下findstr
命令行:
findstr /R /I "^[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ][0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ]*$"
尽管如此,使用管道echo
的整个方法可能仍会失败,因为"
,&
,^
,%
,{{等特殊字符1}},!
,(
,)
,<
,>
可能导致语法错误或其他意外行为。为避免这种情况,我们需要建立delayed expansion,因此特殊字符会从命令解析器中隐藏。但是,由于pipes (|
)初始化任一方的新|
实例(继承当前环境),我们需要确保在左子cmd
实例中执行实际的变量扩展而不是父母一个,像这样:
cmd
启用延迟扩展(:INPUT
set "IN="
set /P IN="Please enter your username: "
cmd /V /C echo(^^!IN^^!| findstr /R /I "^[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ][0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ]*$" > nul || goto :INPUT
)需要额外的显式cmd
实例,因为管道启动的实例已禁用延迟扩展。
只有在父/V
实例中启用了延迟扩展时才需要转义惊叹号^^!
的双倍转义;如果没有,单个转义cmd
就足够了,但转换双倍并不会造成伤害。
for /F
方法这种方法使生活更轻松,因为不涉及管道,因此,您不必处理多个^!
实例,但仍有改进的余地。同样,特殊字符可能会导致麻烦,因此需要启用延迟扩展。
for /F
loop忽略空行,例如以默认cmd
字符分号eol
开头。要禁用;
选项,只需定义其中一个分隔符,即eol
隐藏在eol
后面。空行不会被迭代,因此在用户输入为空的情况下,您的方法中的delims
命令将永远不会执行。因此,我们必须使用if
statement显式捕获空用户输入。现在所有这些都导致以下代码:
goto
此方法仅检测大写字母;要包含小写字母,您必须将它们添加到setlocal EnableDelayedExpansion
:INPUT
set "IN="
set /P IN="Please enter your username: "
if not defined IN goto :INPUT
for /F "delims=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ eol=0" %%Z in ("!IN!") do goto :INPUT
endlocal
选项:delims
。
请注意,变量delims=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
在IN
之后不再可用,但这应该是您脚本的最后一个命令。
要检测是否迭代了endlocal
循环,有一个未记录的功能,我们可以使用它:for /F
如果不迭代则返回非零退出代码,因此可以使用conditional execution operators &&
or ||
;所以,当用户输入为空时,循环不会迭代,然后for /F
;要使其工作,||
循环必须括在括号内:
for /F