如何在Batch脚本中检查一个参数(或变量)是否为数字

26
我需要检查传递给Windows批处理文件的参数是否为数字值。最好能够对变量进行检查。
我发现了一个类似问题的questionanswer,其中使用了带有正则表达式的findstr命令。
我尝试了那个解决方案,但它并没有像我希望的那样工作(至少在Windows 7上不行)。
我的测试场景如下:
AA  # not a valid number
A1  # not a valid number
1A  # not a valid number

11  # a valid number

2
请查看http://www.robvanderwoude.com/sourcecode.php?src=isnumber_nt 直接链接 - David Ruhmann
请注意:其他读者可能会有不同的解读方式。以下一些答案是针对CMD.EXE认可为数字的内容进行测试的。而其他答案则是针对“数字”的其他定义进行测试,例如在原始问题中给出的定义。哪个答案最好取决于个人需求。请明智选择。 :-) - Ben Scott
11个回答

58
SET "var="&for /f "delims=0123456789" %%i in ("%1") do set var=%%i
if defined var (echo %1 NOT numeric) else (echo %1 numeric)

根据需要将%1替换为%yourvarname%


8
+1 但是将 "var=%%i" 用双引号括起来也可以保护一些有害字符。 - foxidrive
3
据我所知,在Windows 10上不再工作了。如果我给%yourvarname%赋一个数字值,就会出现“var未定义”的错误。有什么想法吗? - nst1nctz
1
@nst1nctz:对我来说完美地运作 - 请给出它失败的值的例子。请注意,如果%1是数字,则“var”被设计为未定义,如果%1是数字,则被定义。 - Magoo
2
一个人应该一直质疑自己的代码--我仍在努力学习永远不要相信自己的工作。+1 - nst1nctz
2
@MikeQ:批处理通常是这样的。把它当作一个模板。首先,%1 是批处理过程的第一个参数,因此 call :chknumeric %varname% 会用 varname 的值替换 %1。在活动行前加上标签 :chknumeric,在下一行放置 goto :eof,你就有了一个返回 var 的过程。此例程针对数字字符串,并将处理例如条形码号码之类的内容。如果需要,可以将 var 改为 notnumeric,但 var 可以与 "." 进行比较,以测试单个小数点。或者与不同字符集进行比较,逻辑名称也会改变。 - Magoo
显示剩余3条评论

15

对于 ±整数测试还要包括前导零):

echo(%~1|findstr "^[-][1-9][0-9]*$ ^[1-9][0-9]*$ ^0$">nul&&echo numeric||echo not numeric

1
+1,如果传递了像“this&that”这样的参数,您可能会遇到问题。多做一些工作可以解决这个问题 ;) - dbenham
1
@dbenham 是的。最好的方法是引用并在正则表达式中添加一些点。谢谢。 - Endoro
2
@Endoro:这个命令也可以正确处理毒瘤字符:echo("%~1"|findstr "^[\"][-][1-9][0-9]*[\"]$ ^[\"][1-9][0-9]*[\"]$ ^[\"]0[\"]$">nul&&echo numeric||echo not numeric - Chaoix

11

最简单的正整数应该是:

IF 1%1 NEQ +1%1 echo Notnumeric!

如果也要考虑负数(减号),这个方法也可以使用。
SET number=%1
if 1%1 EQU +1%1 echo positive number
if %1==-%number:-=% echo negative number

这篇文章中了解到:

该文章是关于批处理脚本如何确定变量或参数是否为整数的。

1
了解这一点,但是对其他人来说,请注意,这仅适用于整数。如果您需要支持小数,请参阅其他答案。 - Jerren Saunders
1
哦,这太酷了,谢谢你的发布!(对于整数)这正是我所需要的。非常干净简洁。 - Drac
1
FYI,这在一些边角情况下会失败,但对于CMD来说是可以接受的数字:八进制(020)和十六进制(0x10)。正零(+0)。重复否定(CMD将--5视为5)。这可能对某人的需求不重要,但值得一提。总体而言,它运行得相当不错,适用于少量代码。 - Ben Scott
当参数为空时会失败。将两个参数传递到 test.bat 2 bar 中时,%1 是数字,%2 不是数字,而 %3 是数字。 - Skull Kid

8
你可以尝试这个。传递的变量例如为var,而%var%等于500
set /a varCheck=%var%
if %varCheck% == %var% (goto :confirmed) else (exit /B)
exit /B

:confirmed
:: You can use %var% here, and it should only be executed if it is numerical!

如果%var%等于例如a3453d,它将设置varCheck0,因为0不等于a3453d,所以它会退出批处理。
(第3行的退出仅在if语句由于某些原因决定不执行时使用...XD。)

1
你的解决方案在方案 #3 中失败了。它显示以下错误并终止批处理执行。错误:“无效数字。数值常量可以是十进制(17)、十六进制(0x11)或八进制(021)。 (echo 在此处是意外的。” - mhshams
1
该死..我不知道...我会问我的一个批处理文件专家朋友。 - hornzach

5

@hornzach - 您的回答非常接近,并且比其他人的回答更简单。

要隐藏错误消息,在(至少在win 7中),请将标准错误输出(2)重定向到nul(一个特殊文件,静默丢弃输出“位桶”)

set /a varCheck = %var% 2>nul

那么你的答案对于这四个测试用例都有效。

完整答案及测试用例:

call :CheckNumeric AA  "#NOT A VALID NUMBER"
call :CheckNumeric A1  "#NOT A VALID NUMBER"
call :CheckNumeric 1A  "#NOT A VALID NUMBER"

call :CheckNumeric 11  "#A VALID NUMBER"

call :CheckNumeric 1.23456789012345678901234567890.123456  "#NOT A VALID NUMBER"
goto :EOF

:CheckNumeric
@ECHO.

@ECHO Test %1
set /a num=%1 2>nul

if {%num%}=={%1} (
    @ECHO %1 #A VALID NUMBER, Expected %2
    goto :EOF
)

:INVALID
    @ECHO %1 #NOT A VALID NUMBER, Expected %2

输出:

Test AA
AA #NOT A VALID NUMBER, Expected "#NOT A VALID NUMBER"

Test A1
A1 #NOT A VALID NUMBER, Expected "#NOT A VALID NUMBER"

Test 1A
1A #NOT A VALID NUMBER, Expected "#NOT A VALID NUMBER"

Test 11
11 #A VALID NUMBER, Expected "#A VALID NUMBER"

Test 1.23456789012345678901234567890.123456
1.23456789012345678901234567890.123456 #NOT A VALID NUMBER, Expected "#NOT A VALID NUMBER"

3
这段代码对于所有有前导零的数字(例如05或009)都会产生错误的结果,因为Windows命令处理器使用C函数strtol将字符串转换为32位有符号整数,函数参数base的值为0(自动基数)。因此,我的两个示例的字符串比较为{5}=={05}(去掉前导零)和{}=={009}。变量num对于009仍未定义(或在已经定义的情况下保持其当前值),因为009是一个无效的八进制数。 - Mofi
你可以测试是否存在前导0,例如:if "%SIZE:~0,1%"=="0" (echo 错误: %SIZE% 不是一个有效的数字 & pause & goto :BEGIN) - SSi
好观点@Mofi:+1...但在使用情况中不是必需的:只有十进制数字(假定)。 - 0zkr PM
1
@Cestarian - 这取决于 SET /A 导致 CMD.EXE 评估算术表达式的事实。它在这样的表达式中评估原始变量 %var%,并将结果存储在第二个变量 %varCheck% 中,然后比较这两个变量。如果它们不同,CMD 将其评估为不同的值,这通常意味着原始值是非数字的。例如,如果您要求 CMD 返回字符串 "example" 的数值,CMD 将返回 0(零)。由于 "example"0 不是相同的值,我们可以假设给出了一个非数字值。 - Ben Scott

2

与上面的代码执行相同的操作。但是,存在特殊字符的问题。然而,解决方案采用了不同的方法。

@echo off
cls
setlocal enabledelayedexpansion

if "%1"=="" (goto:eof) else (set _VAR2CHECK=%1)

for /l %%a in (0,1,9) do (
   if defined _VAR2CHECK (set "_VAR2CHECK=!_VAR2CHECK:%%a=!") else (goto :end)
)

:end
if defined _VAR2CHECK (echo %1 #NOT A VALID NUMBER) else (echo %1 #A VALID NUMBER)

set "_VAR2CHECK="
endlocal

Test scenarions:

C:\>ISNUM 1A
1A  #NOT A VALID NUMBER

C:\>ISNUM 11
11  #A VALID NUMBER

2

尝试以1进行除法。 但是对于不属于Z(非相对整数或小数)的数字无法工作。

set var=29
set /a number=%var% >nul 2>&1
set /a number=%number% / 1 >nul 2>&1
if "%var%" == "%number%" (echo numeric) else (echo NOT numeric)

1

if errorlevel 只接受可用于检查的数值:

set test_var=001
( 
  (if errorlevel %test_var% break ) 2>nul
)&&(
  echo %test_var% is numeric
)||(
  echo %test_var% is NOT numeric
)

set test_var=001X
( 
  (if errorlevel %test_var% break ) 2>nul
)&&(
  echo %test_var% is numeric
)||(
  echo %test_var% is NOT numeric
)

第一个检查会通过,第二个不会。 虽然上面的脚本不会处理一元+ - 如果您也想处理这种情况,请检查 isInteger.bat


0

我使用这个函数代替,似乎对我而言没有任何问题...

脚本(mytestscript.bat)

REM == Script variables =============================================
SET "NUMCODE=%1"
SET "NAME=%2"

REM == Main script ==================================================
CALL :Validate "NUMCODE"
IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL%
CALL :Validate "NAME"
IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL%

ECHO Validation Complete and all variables passed validation.

REM == Functions ====================================================
:IsNumeric [string-value] [return-val]
    SET "stringVal=%~1"
    SET /A numericVal=%stringVal% > nul 2> nul
    IF "%stringVal%"=="%numericVal%" (SET "%~2=1") ELSE (SET "%~2=0")
    EXIT /B 0

:Validate
    SET PASSEDVALIDATION=0

    ECHO Validating %~1...

    IF "%~1"=="NUMCODE" (
        ECHO - Value: %NUMCODE%
        IF NOT "%NUMCODE%"=="" SET PASSEDVALIDATION=1
        IF "!PASSEDVALIDATION!"=="1" (
            CALL :IsNumeric %NUMCODE% RETVAL
            SET PASSEDVALIDATION=!RETVAL!
        )
        IF "!PASSEDVALIDATION!"=="1" (
            IF NOT %NUMCODE% GEQ 1 SET PASSEDVALIDATION=0
        )
        IF "!PASSEDVALIDATION!"=="1" (
            IF NOT %NUMCODE% LEQ 526 SET PASSEDVALIDATION=0
        )
        IF "!PASSEDVALIDATION!"=="0" (
            ECHO - The first parameter is invalid, it can not be blank.
            ECHO - Valid value is a number from 1 to 526.
            ECHO - Leading zeros are not valid.
        )
    )

    IF "%~1"=="NAME" (
        ECHO - Value: %NAME%
        IF NOT "%NAME%"=="" SET PASSEDVALIDATION=1
        IF "!PASSEDVALIDATION!"=="0" (
            ECHO - The second parameter is invalid, it can not be blank.
        )
    )

    IF "!PASSEDVALIDATION!"=="0" (
        ECHO - Failed validation.
        EXIT /B 1
    ) ELSE (
        ECHO - Passed validation.
    )
    EXIT /B 0

使用示例:

C:\mytestscript.bat 10 MrTest
Validating NUMCODE...
- Value: 10
- Passed validation.

Validating NAME...
- Value: MrTest
- Passed validation.

C:\mytestscript.bat 600
Validating NUMCODE...
- Value: 600
- The first parameter is invalid, it can not be blank.
- Valid value is a number from 1 to 526.
- Leading zeros are not valid.
- Failed validation.

C:\mytestscript.bat 10
Validating NUMCODE...
- Value: 10
- Passed validation.

Validating NAME...
- Value: 
- The second parameter is invalid, it can not be blank.
- Failed validation.

0

我使用一种更低技术的方法。它仅适用于整数并支持负值。

set "XVALUE=%~1"
set /A XVALUE=XVALUE
if "%XVALUE%" NEQ "%~1" goto :invalid_value

第一行获取命令行参数并将其放入XVALUE中。使用“引号”可以避免各种特殊字符的问题。

第二行看起来很神秘,但利用了SET /A等号右侧有文本字符串时被解释为包含数字值的环境变量的特性。命令处理器解析假定的数字值而不生成任何错误消息或设置ERRORLEVEL。以这种方式执行始终会将XVALUE转换为有效的数字值,无论传递给我们的是什么。

第三行是可选的,只有在您想要严格限制参数为-2147483648到2147483647范围内的数字值时才需要使用if测试。

  • 通过在数字前加上0或0x,您可以获得八进制和十六进制数字的支持。
  • 如果有人使用非常小或非常大的数字,例如-9999999999999或9999999999999,则XVALUE将被设置为允许的最小或最大值,即-2147483648和2147483647。
  • 如果有人使用类似“hello”这样的垃圾,则XVALUE将被设置为0。如果是类似于123x456这样的内容,则解析器会在“x”处停止,并将XVALUE设置为123。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接