0

I need to count the characters of an inputed string in Batch. I don't want to use temporary files. Could it be done without them? If yes, explanations of your code would be greatly appreciated. Thanks SO!

Ivan Spajić
  • 159
  • 3
  • 5
  • 14

2 Answers2

3

A simple way is to use a function

@echo off
set "myVar=abcdefg"
call :Stringlength result myVar
echo %result%
exit /b

:Stringlength <resultVar> <stringVar>
(   
    setlocal EnableDelayedExpansion
    set "s=!%~2!#"
    set "len=0"
    for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
        if "!s:~%%P,1!" NEQ "" ( 
            set /a "len+=%%P"
            set "s=!s:~%%P!"
        )
    )
)
( 
    endlocal
    set "%~1=%len%"
    exit /b
)

This can measure the string to a maximum of 8192 characters, as the maximum size of a string is 8191 bytes, this should be enough.
The first parenthesis blocks is only for a bit more performance.
The second block is needed to return the %len% value behind the endlocal barrier.
The main idea is a binary search, in the first loop the temporary copy in s of the string is tested if it is longer than 4096 bytes or not.
Then the next test will be with 2048 or 6144 (=2048+4096), so the len variable will be at each loop a little bit more exact.
After 13 loops the len is exact.

For faster strlen functions you could read strlen boosted, which uses some more tricks.

There is also a solution with batch macros, macros are normally much faster than functions in batch.

@echo off
call :loadMacros
set "myVar=abcdefg"
%$strlen%  result myVar
echo %result%
exit /b


:loadMacros
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
:::: StrLen pResult pString
set $strLen=for /L %%n in (1 1 2) do if %%n==2 (%\n%
        for /F "tokens=1,2 delims=, " %%1 in ("!argv!") do (%\n%
            set "str=A!%%~2!"%\n%
              set "len=0"%\n%
              for /l %%A in (12,-1,0) do (%\n%
                set /a "len|=1<<%%A"%\n%
                for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"%\n%
              )%\n%
              for %%v in (!len!) do endlocal^&if "%%~b" neq "" (set "%%~1=%%v") else echo %%v%\n%
        ) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,

exit /b

At dostips.com are some discussion about the macro technic
1 Batch "macros" with arguments
2 macros with appended parameters

jeb
  • 78,592
  • 17
  • 171
  • 225
  • I'm sure this is faster than a basic loop but you are making assumptions about the maximum string length... – Anders May 20 '12 at 16:42
  • What is the point of the two main blocks in the function and their braces? – Anders May 20 '12 at 16:43
  • Yes I make the assumption that the string can't be longer than 8192 bytes, and that's always true, as a variable can't contain more – jeb May 20 '12 at 16:44
  • Why does the first block give better performance? The second block can probably be replaced by endlocal&set ... – Anders May 20 '12 at 16:53
  • Can someone explain the whole thing to me? I seem quite baffled by it all. I would like a top-to-down, thorough explanation. Thanks! – Ivan Spajić May 20 '12 at 18:58
  • Also, I tried making a Batch file out of it and it doesn't do anything but print pretty much everything that is written there. I need some help understanding this. Thank you! – Ivan Spajić May 20 '12 at 19:00
  • @Ivan Spajic - Then your first line should be an `@echo off` – jeb May 20 '12 at 20:10
  • @Anders - It's a caching issue, but this isn't the fastest stlen function, we made many experiments with funtions and macros to build the _best_ one, see the edited answer and links – jeb May 20 '12 at 20:10
  • +1 for the links alone, batch files sure are fun :) – Anders May 20 '12 at 21:29
0

When you call the function, the 2nd parameter should be a value rather than a reference:

call :Stringlength result %myVar%
andy_t
  • 1