Batch Regular Expressions

I am writing a calculator on cmd with binary operations included. I need to validate input data (remove letters and other characters that are not required for arithmetic operations)

@echo off
set data=

echo %* | findstr /R "\/\? ECHO" > nul
IF "%ERRORLEVEL%" EQU "0" goto printHelp

:main
set data= %data%%1
shift

if "%1" == "" (
    echo %data% | findstr /R "^[0123456789*-+()/%!^_&|]*$" >nul 2>&1
    if "%ERRORLEVEL%" EQU 0 (
        echo Incorrect input data
        exit /B
    )

    goto :result
) else (
    goto :main
)

:result
    set /a data="%data%"
    echo %data%
    exit /B

:printHelp
    echo.
    echo --------------------------------------------------
    echo Using: calculator.bat [/?] [EXPRESSION]
    echo helps you to consider arithmetic in Command Line
    echo --------------------------------------------------
    exit /B

      

My regex doesn't work. It is also not considered a binary operation. What could be the problem?

+3


source to share


1 answer


Part 1 - Why Your "Regular Expression Won't Work"

  • Your logic is wrong. FINDSTR sets ERRORLEVEL to 0 if there is a match, 1 if there is no match. Your regular expression checks that all characters are "valid", but your state treats the match as invalid input.

  • Your IF statement uses quotation marks on one side, but not on the other. You have to be consistent or it will never evaluate to TRUE.

  • Percentage literals must be doubled in script batch. Your regex has a percent literal which should be written as %%

    .

  • You are using% ERRORLEVEL% in the same code block that sets the value. This cannot work because the value is expanded when the block of code is parsed - before the value is set.

    The simplest alternative is to use if errorlevel 1

    that returns true if ERRORLEVEL =>.

    Another option is to enable delayed expansion with SETLOCAL ENABLEDELAYEDEXPANSION at the top and then use if !errorlevel! neq 0

    . But that would require the quoted !

    literal in your regex to be escaped as ^!

    , and the ^

    literal escaped as ^^

    .

    My favorite option is to use conditional statements &&

    and ||

    instead IF.

    findstr ... >nul && (match found statements) || (no match statements)

    In your case, you want to take action if there is no match, so you only need an operator ||

    .

Part 2 - Why Your Concept Isn't a Good Idea

  • Your check is too simplistic. Simply escaping invalid characters does not prevent errors. For example, 1**2

    will result in an error even if all characters are "valid". There are many other entries with "valid" characters that can lead to errors.

  • SET / A can work directly with environment variables. He knows how to access the value without extending your code. This can be a powerful tool. The variable name used in the calculation can include any character that is not an operator. Therefore, it can be argued that there are no invalid characters for SET / A computations. Your elimination of "invalid" characters prevents variables from being used in calculations.


Below is a simple batch calculation program that I wrote a while ago. It loops indefinitely, asking for input and displaying the result, until you issue the quit command. It supports all the operators supported by SET / A.

It allows you to define and use variables in your expressions. The result of the most recent calculation is always stored in a named variable #

.

The calculator can display results as decimal, hexadecimal, or binary.

By default, it only displays the result of the last calculation. With each calculation, you can also specify the value of all variables.



You can enter a command instead of a mathematical calculation. All commands start with\

\

Exit
\V

Toggle the list of variable variables on or off
\D

Decimal mode - results are displayed as decimal
\H

Hex mode - results are displayed as hexidecimal
\B

Binary mode - results are displayed as binary

\C X

Clear variable X
\C *

Remove all variables
\C X*

Clear all variables starting with X

Typing nothing will display all the current variables.

The resettable variables are undefined. Note that the variable undefined has an implicit value of 0.

Here is the code:

@echo off
setlocal enableDelayedExpansion
for /f "delims==" %%v in ('set') do set %%v=
set __skip=#COMSPEC#PATHEXT#PROMPT#__mode#__str#__skip#__clr###__dispVars#
set __mode=Dec
set __dispVars=0

:top
echo:
set __str=
set /p "__str=%__mode%> "
if "!__str!"=="\" exit /b
if "!__str!"=="" call :dispVar # & call :dispVars & goto :top
if /i "!__str:~0,2!"=="\C" call :clearVars &goto :top
if /i "!__str!"=="\H" (set __mode=Hex) ^
else if /i "!__str!"=="\D" (set __mode=Dec) ^
else if /i "!__str!"=="\B" (set __mode=Bin) ^
else if /i "!__str!"=="\V" (set /a "__dispVars=^!__dispVars") ^
else set /a #=(!__str!)
call :dispVar #
if !__dispVars! gtr 0 call :dispVars
goto :top

:clearVars
  for /f "delims=,; " %%v in ("!__str:~2!") do (
    set __clr=%%v
    if "!__clr:~-1!"=="*" (
      set __clr=!__clr:~0,-1!
      for /f "delims==" %%x in ('set !__clr!') do (
        if "!__skip:#%%x#=!"=="!__skip!" set "%%x="
      )
    ) else set "%%v="
  )
  call :dispVar #
  call :dispVars
exit /b

:dispVars
  setlocal
  for /f "tokens=1,2 delims==" %%v in ('set') do if "!__skip:#%%v#=!"=="!__skip!" call :dispVar %%v
exit /b

:dispVar Var
  setlocal
  if !__mode!==Hex call :num2hex %1 disp
  if !__mode!==Bin call :num2bin %1 disp
  if !__mode!==Dec set /a disp=!%~1!
  set var=%~1
  if "!var:~0,6!"=="!var!" (
    set "var=!var! ----------"
    set "var=!var:~0,6!"
  )
  echo %var% = !disp!
exit /b

:num2hex    NumVal RtnVar
  setlocal enabledelayedexpansion
  set hex=
  set /a "dec=%~1"
  set "map=0123456789ABCDEF"
  for /l %%n in (1,1,8) do (
    set /a "d=dec&15,dec>>=4"
    for %%d in (!d!) do set "hex=!map:~%%d,1!!hex!"
  )
  (endlocal & rem return values
    set %~2=%hex%
    exit /b
  )
exit /b

:num2bin    NumVal RtnVar
  setlocal enabledelayedexpansion
  set bin=
  set /a "dec=%~1"
  for /l %%n in (1,1,32) do (
    set /a "d=dec&1,dec>>=1"
    set "bin=!d!!bin!"
  )
  (endlocal & rem return values
    set %~2=%bin%
    exit /b
  )
exit /b

      

And here are the results from a short session:

D:\test>calculate.bat

Dec> 2*3
# ---- = 6

Dec> a=#+1
# ---- = 7

Dec>
# ---- = 7
a ---- = 7

Dec> b=(a+=5)*2
# ---- = 24

Dec> \v
# ---- = 24
a ---- = 12
b ---- = 24

Dec> c=b/3
# ---- = 8
a ---- = 12
b ---- = 24
c ---- = 8

Dec> \h
# ---- = 00000008
a ---- = 0000000C
b ---- = 00000018
c ---- = 00000008

Hex> \b
# ---- = 00000000000000000000000000001000
a ---- = 00000000000000000000000000001100
b ---- = 00000000000000000000000000011000
c ---- = 00000000000000000000000000001000

Bin> \

D:\test>

      

+2


source







All Articles