6

I am new to batch scripting. although i have shell and python solutions for this problem but got stuck in batch script.

I have string like 123_happy, 234.healthy, 3456wealthy etc.

I want to extract the leading numbers from each string. the only pattern here is that all these strings contain numbers in the beginning.

I can't use echo %str:~0,3% as it doesn't fulfill my requirement.

2
  • 1
    I note that the third example you've provided does not begin with a leading number, it begins with a double quote. Does this mean that you intend to completely ignore any doublequote character at any position in any string? Commented Sep 17, 2016 at 9:02
  • 1
    hey, i have corrected that. its as same as the other entries. without quotes. thanks for pointing. Commented Sep 17, 2016 at 9:26

6 Answers 6

12

I think this is the simplest way:

@echo off
setlocal

set "str=123_happy"
set /A num=str
echo Result = %num%

When set /A command get a value from a variable, it convert the number until the first non-digit character with no error.

To preserve left zeros:

set "str=0128_happy"
set "num=1%str%"
set /A num=num
set "num=%num:~1%"
echo Result = %num%
Sign up to request clarification or add additional context in comments.

3 Comments

@dbenham: Did you tested it? Try: set str=123-456 & set /A num=str & echo "%num%". The output is: "123" (at least, it is in my Win 8.1 computer).
Oops, sorry. I didn't look carefully enough and assumed you were expanding the variable. You are correct, it works just fine. I'll delete my comment..
Although I like this approach pretty much, it is limited to signed 32-bit integers and, when preserving leading zeros, to even less, namely [0,999999999]...
7

Just another option

@echo off
    setlocal enableextensions disabledelayedexpansion

    call :extractLeadingNumbers 123_happy leadingNumbers
    echo %leadingNumbers%

    call :extractLeadingNumbers 234.healthy leadingNumbers
    echo %leadingNumbers%

    call :extractLeadingNumbers "3456wealthy" leadingNumbers
    echo %leadingNumbers%

    goto :eof


rem This extracts the first numerical serie in the input string    
:extractLeadingNumbers inputString returnVar
    setlocal enableextensions disabledelayedexpansion
    rem Retrieve the string from arguments
    set "string=%~1"

    rem Use numbers as delimiters (so they are removed) to retrieve the rest of the string
    for /f "tokens=1-2 delims=0123456789 " %%a in ("%string:^"=%") do set "delimiters=%%a%%b"

    rem Use the retrieved characters as delimiters to retrieve the first numerical serie
    for /f "delims=%delimiters% " %%a in ("%string:^"=%") do set "numbers=%%a"

    rem Return the found data to caller and leave
    endlocal & set "%~2=%numbers%"
    goto :eof

Comments

7

probably the easiest and the fastest way:

set z=123_happy
for /l %%# in (%z%,1,%z%) do echo %%#

this will leave only the leading numbers.Though it is limited to 32b integers. As a subroutine (will fail if the input contains delimiters):

:extractLeadingNumbers input [rtrnVar]
  for /l %%# in (%~1;1;%~1) do (
     if "%~2" neq "" (
       set "%%#=%~2"
     ) else (
       echo %%#
     )
  )

More robust way (which will also remove leading zeroes):

cmd /c exit /b 123_happy
echo %errorlevel%

7 Comments

I'd be inclined to change it to ) do if %%# geq 1 echo %%# so that strings not beginning with a number do not look as if they begin with a 0.
For the more robust method then If ErrorLevel 1 Echo(%ErrorLevel%.
@Jean-FrançoisFabre - the cmd exit works fine with octals.
@npocmaka: it works but strips the leading zeroes. To avoid that you could do set z=00123_happy cmd /c exit /b 1%z% echo %errorlevel:~1,1000%
Instead of cmd /c it may be quicker to call a sub (which doesn't start a whole new process and can also use exit /b)
|
4

Loop (1=>1000 should be enough ;)) on the characters of the variable and find the first char not in number range. Extract the substring before:

@echo off

set z=123_happy
setlocal enabledelayedexpansion
set result=

for /L %%i in (0,1,1000) do (
set zz=!z:~%%i,1!
if x!zz!==x exit /b
if !zz! lss 0  (
set result=!z:~,%%i!
goto out
)
if !zz! gtr 9  (
set result=!z:~,%%i!
goto out
)
)
:out
echo result=!result!

result:

result=123

3 Comments

proper coding with batch files IS complicated. My advice: stick to python unless you really can't do otherwise.
This "solution" is extremely inefficient; there are much simpler ways to get the same result (see the other answers). Proper coding with Batch files is NOT complicated per se. Any program written without correct use of language features is complicated, no matters the programming language used.
@Aacini your answer is the less "hacky" one in that particular case of only reading the numbers (I suppose MS-DOS commands use atoi-style primitives so they parse the leading integer and ignore the rest. In the case of batch files, language features are really limited, which makes it more complicated than bash, awk, python, perl... but it's the built-in windows shell. Writing a .bat file guarantees it works on any Windows machine regardless of the installed software. That's the main point.
2

I prefer Jean-François's answer, but the following is an alternative, hacky way of doing it. It relies on testing for success or failure of the set /a command. We test increasingly longer starting parts of the string until we fail, and then we know the previous attempt was the right one.

@echo off
rem Prints the numbers in front of a string variable.
rem IMPORTANT - this batch file must have a .cmd extension (NOT a .bat)
setlocal enabledelayedexpansion
call :getnumbers 123_happy
call :getnumbers 234.healthy
call :getnumbers 3456wealthy
exit /b

:getnumbers
set s=%1
for /l %%i in (1 1 999) do (
    set substr=!s:~0,%%i!
    set /a n=!substr! 2>nul
    if errorlevel 1 goto :getnumbers_exitloop
)
:getnumbers_exitloop
echo The number in front of %1 is %n%

2 Comments

problem is: there's a catch: try this: set /A x=087. It fails because of leading zero and octal, but it's still a valid numerical prefix.
set /a n=1!substr! 2>nul would work, though (adding 1 just in case there is a leading zero)
1

What about this:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define the string here:
set "STR=123_happy"

setlocal EnableDelayedExpansion
rem // Get first character behind the numeric part:
for /F delims^=0123456789^ tokens^=*^ eol^= %%F in ("!STR!") do (
    endlocal
    set "SEP=%%F"
    setlocal EnableDelayedExpansion
    if defined SEP set "SEP=!SEP:~,1!"
)

rem // Split the string at the character behind the numeric part:
if not defined SEP goto :SKIP
for /F eol^=^%SEP%^ delims^=^%SEP% %%N in ("0!STR!") do (
    endlocal
    set "STR=%%N"
    setlocal EnableDelayedExpansion
    set "STR=!STR:~1!"
)
:SKIP

rem // Return the numeric part:
echo(!STR!
endlocal

endlocal
exit /B

The basic idea is to get the first character after the numeric part, which is the used as the delimiter for a for /F loop parsing the input string. This has got the great advantage that the limit for signed 32-bit integers does not apply, opposed to the approaches using set /A or for /L. In addition, leading zeros do not lead to unintentional interpretation as octal numbers, since this script treats the numeric part as string.

1 Comment

Tested (and fixed) the code meanwhile -- see my edit...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.