哪种正则表达式方法最适合验证用户输入? (for / f with delims vs echo%var%| Findstr / ri)

时间:2017-06-01 23:59:20

标签: regex batch-file for-loop findstr

我想验证用户的输入并仅将输入限制为字母数字字符(也可以允许使用下划线),但我不确定哪种方法最适合此。

我见过关于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)优势?

2 个答案:

答案 0 :(得分:2)

首先,您必须使用延迟扩展来引用环境变量in,以避免因用户输入包含><|&"等关键字符的字符串时出现语法错误而导致批处理文件执行退出。始终考虑在执行命令行之前扩展用%variable%指定的变量,这很容易破坏用户输入变量字符串的批处理执行。

其次,强烈建议在提示后立即验证用户是否输入了任何内容,即在提示命令行后使用if not defined in goto input

第三,我认为 FOR 方法因为速度更快而更好。

FINDSTR 不是cmd.exe的内部命令,例如 FOR 。因此,当在没有路径且没有文件扩展名的批处理文件中指定 FINDSTR 时,Windows命令解释程序必须首先搜索此可执行文件,并希望通过%SystemRoot%\System32\findstr.exePATHEXT真正找到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]匹配小写字母bz[a-z]匹配大写字母AY(当然这在不区分大小写的搜索的情况下无关紧要,所以当给出/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=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzIN之后不再可用,但这应该是您脚本的最后一个命令。

要检测是否迭代了endlocal循环,有一个未记录的功能,我们可以使用它:for /F如果不迭代则返回非零退出代码,因此可以使用conditional execution operators && or ||;所以,当用户输入为空时,循环不会迭代,然后for /F;要使其工作,||循环必须括在括号内:

for /F