#1 20 Jul 2008 17:06

Cornan
Member
Registered: 20 Jul 2008
Posts: 9

stringlength.cmd - just created - for anybody who can use it

Just created this stringlength.cmd file.  All have my permission to use and SS64.org has my permission to post it

Usage: CALL stringlength  any parameters

Return: _stringlength variable set to total parameter length

@ECHO OFF
SETLOCAL
SET WhatsLeft=%*
SET Count=0
:TestIt
IF "%WhatsLeft%"=="" GOTO RETURN
SET WhatsLeft=%WhatsLeft:~,-1%
SET /A Count=%Count%+1
GOTO TestIt
:RETURN
ENDLOCAL & SET _stringlength=%Count%

Offline

#2 20 Jan 2013 17:55

npocmaka
Member
From: Bulgaria
Registered: 03 Dec 2009
Posts: 421

Re: stringlength.cmd - just created - for anybody who can use it

An pretty old topic ,but I also needed this.
I've used almost the same thing , but in this above I see two potential problems - If the string contains double quotes the if checks may fail , and there's no initial check if the string is empty.Here I'm trying to deal with this (but not completely works with the second parameter may I should remove it as an option):

:strlen [%1 -string, %2 variable to store result in]
setlocal
set string=%~1
rem if the string contains double quotes it will harm the IF checks
set string=%string:"=.%
set var=%~2
set /a counter=0
if "%string%" equ "" goto :return
:loop 
	rem counter will hold the 1/2 of the string lenght
	set /a counter=%counter%+1
	set string=%string:~1,-1%
	if "%string%" equ "" (
		goto :endloop
	)

goto :loop
:endloop

set string=%~1
set string=%string:"=.%

set /a counter=2*%counter%-1
rem checking  if the string is with even or with uneven lenght 
call set string=%%string:~%counter%%%
if "%string%" neq "" set /a counter=%counter%+1
echo %counter%
:return
endlocal & set %var%=%counter% >nul  2>&1

I'm also trying to "eat" the string from two directions with a tiny hope that this will be faster for a long strings.

Offline

#3 21 Jan 2013 01:39

dbenham
Member
From: U.S. east coast
Registered: 15 Apr 2012
Posts: 109

Re: stringlength.cmd - just created - for anybody who can use it

Here is a survey of algorithms for computing string length. One general technique in common is to use delayed expansion so that the routines compute the correct length no matter what characters are contained within the string.

Each of the routines below has identical functionality and nearly identical limits. The only slight differences are the maximum length that can be computed. All methods approach the 8191 maximum byte length, but the actual limit varies slightly.

Each routine requires the name of a variable as the first parameter. Each routine measures the length of the string value stored within the named variable, and properly computes 0 if the variable is not defined.

Each routine optionally takes the name of a return variable as the 2nd parameter, where the result is stored. If the return variable is not specified, then the result is ECHOed to stdout.

I placed each routine in its own file to get accurate timings. For each routine, I report the amount of seconds it takes to compute the length of various string lengths 1000 times.


:strLen1
The simplest (and generally slowest) is a brute force linear technique that is basically the same as the original post in this thread. It is fine for short strings, but is awful for even moderately long strings. The problem is that GOTO is quite slow. The routine is even worse if the routine is embedded in a long batch file, because batch must scan the entire file to loop back - the longer the file, the worse the performance.

:strlen1  StrVar  [RtnVar]
  setlocal enableDelayedExpansion
  set "s=!%~1!"
  set len=0
  :strLenLoop
  if defined s (
    set "s=!s:~1!"
    set /a len+=1
    goto :strlenLoop
  )
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

length   sec*1000
     0       4.35
     5       7.98
   326     239.5
  8184    7147.

:strLen2
This simple optimization uses a FOR /L loop instead of GOTO to perform the brute force linear length search. It makes short strings a bit slower, but is a major improvement for moderately long strings. It is still quite slow for very long strings.

:strlen4  StrVar  [RtnVar]
  setlocal enableDelayedExpansion
  set "s=!%~1!"
  set len=0
  if defined s for /l %%N in (1 1 8192) do if "!s:~%%N,1!" equ "" (
	  set len=%%N
    goto :break
  )
  :break
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

length   sec*1000
     0       4.28
     5      82.90
   326      86.15
  8184     429.0

:strLen3
Here is the first algorithm that has decent performance that does not degrade with long strings. It uses the rarely used FINDSTR /O option. The limiting performance factor is the startup time for the external FINDSTR command and the extra time it takes to launch 2 CMD threads.

:strLen3  StrVar  [RtnVar]
  setlocal disableDelayedExpansion
  set len=0
  if defined %~1 for /f "delims=:" %%N in (
    '"(cmd /v:on /c echo(!%~1!&echo()|findstr /o ^^"'
  ) do set /a "len=%%N-3"
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

length   sec*1000
     0       6.33
     5      40.23
   326      40.05
  8184      41.86

:strLen4
The remainder of the algorithms all have excellent performance. This one is amazingly simple. Its only drawback in some people's eyes is its use of a temporary file.

:strlen4  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "tempFile=%temp%\strlen%random%.tmp"
  echo(!%~1!>"%tempFile%"
  for %%F in ("%tempFile%") do set /a len=%%~zF-2
  del "%tempFile%"
  endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

length   sec*1000
     0       6.33
     5       6.44
   326       6.31
  8184       7.27

:strLen5
This is a minor optimization of :strLen4 that on my machine gives the best performance. The only difference is it assumes the name of the temporary file is already initialized, and it does not bother deleting the temporary file when finished. The same temp file gets reused for each call.

:strlen2  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  echo(!%~1!>"%tempFile%"
  for %%F in ("%tempFile%") do set /a len=%%~zF-2
  endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

length   sec*1000
     0       4.73
     5       4.72
   326       4.75
  8184       5.62

:strLen6
This is a very fast algorithm developed at DosTips. It uses a binary search to detect the length of the string. It only requires 13 iterations for any length string supported by batch. On some machines, this routine is slightly faster than :strlen4, but on my machine it is a bit slower. The biggest advantage over :strLen4 and :strLen5 is this routine does not need a temp file.

:strlen1  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "s=#!%~1!"
  set "len=0"
  for /l %%A in (12,-1,0) do (
    set /a "len|=1<<%%A"
    for %%B in (!len!) do if "!s:~%%B,1!"=="" set /a "len&=~1<<%%A"
  )
  endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

length   sec*1000
     0       8.04
     5       7.96
   326       7.66
  8184       9.27

:strLen7
This is a minor optimization of :strLen6 that is slightly faster. Still not quite as fast as :strLen5 on my machine.

:strlen0  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "s=#!%~1!"
  set "len=0"
  for %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
    if "!s:~%%N,1!" neq "" (
      set /a "len+=%%N"
      set "s=!s:~%%N!"
    )
  )
  endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

length  sec*1000
     0      4.53
     5      5.07
   326      5.56
  8184      7.84

Dave Benham

Last edited by dbenham (21 Jan 2013 02:12)

Offline

#4 21 Jan 2013 15:34

npocmaka
Member
From: Bulgaria
Registered: 03 Dec 2009
Posts: 421

Re: stringlength.cmd - just created - for anybody who can use it

Hohoooo..
Cool.
A small improvement over strlen2 that uses again the slicing from two directions.
I didn't performed any performance checks yet but hope it will be faster:

:strlen2.5  StrVar  [RtnVar]
  setlocal enableDelayedExpansion
  set "s=!%~1!"
  set len=0
  if defined s for /l %%N in (1,1,8192) do if "!s:~%%N,-%%N!" equ "" (
	  set len=%%N
    goto :break
  )
  :break
  set /a len=2*!len!-1
  for %%E in (!len!) do (
	set s=!s:~%%E!
  )
  if "!s!" neq "" set /a len=!len!+1
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

Last edited by npocmaka (22 Jan 2013 00:11)

Offline

#5 21 Jan 2013 20:15

npocmaka
Member
From: Bulgaria
Registered: 03 Dec 2009
Posts: 421

Re: stringlength.cmd - just created - for anybody who can use it

and the same idea as the 7th function but based on powers of 3:

:strlen0.3  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "s=#!%~1!"
  set "len=0"
  for %%A in ( 6561 2187 729 243 81 27 9 3 1) do (
    set /A mod=2*%%A
    for %%Z in (!mod!) do (
        if !mod! GTR 8190 (
			set mod=8190
		)
		if "!s:~%%Z,1!" neq "" (
            set /a "len+=%%Z"
            set "s=!s:~%%Z!"

        ) else (
            if "!s:~%%A,1!" neq "" (
                set /a "len+=%%A"
                set "s=!s:~%%A!"
            )
        )
    )
  )

Have no idea how fast  this is :-)
(may be it's worth to try also with 4 - it will require one more nested IF and one more nested FOR and the IF checks will be almost the same number as the loops in the outer FOR.Also could try to put two item in inner FOR instead of nested IF but this will require one GOTO ....)

Last edited by npocmaka (21 Sep 2014 18:01)

Offline

#6 25 Jan 2013 10:27

npocmaka
Member
From: Bulgaria
Registered: 03 Dec 2009
Posts: 421

Re: stringlength.cmd - just created - for anybody who can use it

and one more - just for fun again based on strlen2  (but with 5 hops on each iteration):

:strlen2.9  StrVar  [RtnVar]
  setlocal enableDelayedExpansion
  set "s=!%~1!"
  set len=0  
  if defined s for /l %%N in (1,5,8192) do if "!s:~%%N,-%%N!" equ "" (
	  set len=%%N
    goto :break
  ) 
  :break
 
  if !len! gtr 1 (
	set /a len=2*!len!-12
	for %%E in (!len!) do (
		set s=!s:~%%E!
	)
  )
  
  if defined s (
    if "!s:~0!" neq "" set /a len=!len!+1
	if "!s:~1!" neq "" set /a len=!len!+1
	if "!s:~2!" neq "" set /a len=!len!+1
	if "!s:~3!" neq "" set /a len=!len!+1
	if "!s:~4!" neq "" set /a len=!len!+1
	if "!s:~5!" neq "" set /a len=!len!+1
	if "!s:~6!" neq "" set /a len=!len!+1
	if "!s:~7!" neq "" set /a len=!len!+1
	if "!s:~8!" neq "" set /a len=!len!+1
	if "!s:~9!" neq "" set /a len=!len!+1
	if "!s:~10!" neq "" set /a len=!len!+1
	if "!s:~11!" neq "" set /a len=!len!+1
  )

  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

It was a little bit harder for implementation than I thought  before start writing it.
As I was curious about how fast are mine - here's an evaluation script:

::::::::::::
::
:: strlen evaluation
::
:::::::::::
@echo off
set str1=1
set str5=11111
set str10=1111111111
set str50=11111111111111111111111111111111111111111111111111
set str100=1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
set str1000=1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
set str5000=11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111




echo\
echo  -----------------
echo  --- strlen2.9 ---
echo  -----------------
echo\
call :timecalc strlen2.9 str5
call :timecalc strlen2.9 str1
call :timecalc strlen2.9 str10
call :timecalc strlen2.9 str50
call :timecalc strlen2.9 str100
call :timecalc strlen2.9 str1000
call :timecalc strlen2.9 str5000


echo\
echo  -----------------
echo  --- strlen1 ---
echo  -----------------
echo\
call :timecalc strlen1 str5
call :timecalc strlen1 str1
call :timecalc strlen1 str10
call :timecalc strlen1 str50
call :timecalc strlen1 str100
call :timecalc strlen1 str1000
call :timecalc strlen1 str5000

echo\
echo  -----------------
echo  --- strlen2.5 ---
echo  -----------------
echo\
call :timecalc strlen2.5 str5
call :timecalc strlen2.5 str1
call :timecalc strlen2.5 str10
call :timecalc strlen2.5 str50
call :timecalc strlen2.5 str100
call :timecalc strlen2.5 str1000
call :timecalc strlen2.5 str5000

echo\
echo  -----------------
echo  --- strlen2 ---
echo  -----------------
echo\
call :timecalc strlen2 str5
call :timecalc strlen2 str1
call :timecalc strlen2 str10
call :timecalc strlen2 str50
call :timecalc strlen2 str100
call :timecalc strlen2 str1000
call :timecalc strlen2 str5000

echo\
echo  -----------------
echo  --- strlen3 ---
echo  -----------------
echo\
call :timecalc strlen3 str5
call :timecalc strlen3 str1
call :timecalc strlen3 str10
call :timecalc strlen3 str50
call :timecalc strlen3 str100
call :timecalc strlen3 str1000
call :timecalc strlen3 str5000

echo\
echo  -----------------
echo  --- strlen0.3 ---
echo  -----------------
echo\
call :timecalc strlen0.3 str5
call :timecalc strlen0.3 str1
call :timecalc strlen0.3 str10
call :timecalc strlen0.3 str50
call :timecalc strlen0.3 str100
call :timecalc strlen0.3 str1000
call :timecalc strlen0.3 str5000

echo\
echo  -----------------
echo  --- strlen0 ---
echo  -----------------
echo\
call :timecalc strlen0 str5
call :timecalc strlen0 str1
call :timecalc strlen0 str10
call :timecalc strlen0 str50
call :timecalc strlen0 str100
call :timecalc strlen0 str1000
call :timecalc strlen0 str5000




goto :eof


:timecalc 

set time1=%time%
call :%1 %2 len
set time2=%time%
call :gettimeinms %time1% ms1
call :gettimeinms %time2% ms2
if %ms1% gtr  15900 (
	if %ms2% lss 11000 (
		set /a ms2=%ms2%+6000
	)
)
set /a "totalTime=%ms2%-%ms1%"
echo %2 -^> %totalTime%    [ %time2% - %time1% ]
goto :eof


:gettimeinms
setlocal
for /f "tokens=3,4 delims=:."  %%S in ( "%~1") do (
	set ms=1%%S%%T
)

endlocal & set %~2=%ms%
goto :eof



::::::::::::::::::::::::::::::::::::::::
::   strlen1
:strlen1  StrVar  [RtnVar]
  setlocal enableDelayedExpansion
  set "s=!%~1!"
  set len=0
  :strLenLoop
  if defined s (
    set "s=!s:~1!"
    set /a len+=1
    goto :strlenLoop
  )
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

::length   sec*1000
::     0       4.35
::     5       7.98
::   326     239.5
::  8184    7147.
::::::::::::::::::::::::::::::::::::::::



::::::::::::::::::::::::::::::::::::::::
::  strlen4 
:strlen4  StrVar  [RtnVar]
  setlocal enableDelayedExpansion
  set "s=!%~1!"
  set len=0
  if defined s for /l %%N in (1 1 8192) do if "!s:~%%N,1!" equ "" (
	  set len=%%N
    goto :break
  )
  :break
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

::length   sec*1000
::     0       4.28
::     5      82.90
::   326      86.15
::  8184     429.0
:::::::::::::::::::::::::::::::::::::::::::


::::::::::::::::::::::::::::::::::::::::
::  strlen3 
:strLen3  StrVar  [RtnVar]
  setlocal disableDelayedExpansion
  set len=0
  if defined %~1 for /f "delims=:" %%N in (
    '"(cmd /v:on /c echo(!%~1!&echo()|findstr /o ^^"'
  ) do set /a "len=%%N-3"
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

::length   sec*1000
::     0       6.33
::     5      40.23
::   326      40.05
::  8184      41.86
::::::::::::::::::::::::::::::::::::::

::::::::::::::::::::::::::::::::::::::::
::  strlen4 
:strlen4  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "tempFile=%temp%\strlen%random%.tmp"
  echo(!%~1!>"%tempFile%"
  for %%F in ("%tempFile%") do set /a len=%%~zF-2
  del "%tempFile%"
  endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

:: length   sec*1000
::     0       6.33
::     5       6.44
::   326       6.31
::  8184       7.27
::::::::::::::::::::::::::::::::::::::

::::::::::::::::::::::::::::::::::::::::
::  strlen2 
:strlen2  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  echo(!%~1!>"tempFile"
  for %%F in ("tempFile") do set /a len=%%~zF-2
  endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

::length   sec*1000
::     0       4.73
::     5       4.72
::   326       4.75
::  8184       5.62
::::::::::::::::::::::::::::::::::::::

::::::::::::::::::::::::::::::::::::::::
::  strlen1 
:strlen1  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "s=#!%~1!"
  set "len=0"
  for /l %%A in (12,-1,0) do (
    set /a "len|=1<<%%A"
    for %%B in (!len!) do if "!s:~%%B,1!"=="" set /a "len&=~1<<%%A"
  )
  endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

::length   sec*1000
::     0       8.04
::     5       7.96
::   326       7.66
::  8184       9.27
::::::::::::::::::::::::::::::::::::::

::::::::::::::::::::::::::::::::::::::::
::  strlen0 
:strlen0  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "s=#!%~1!"
  set "len=0"
  for %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
    if "!s:~%%N,1!" neq "" (
      set /a "len+=%%N"
      set "s=!s:~%%N!"
    )
  )
  endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

::length  sec*1000
::     0      4.53
::     5      5.07
::   326      5.56
::  8184      7.84

::::::::::::::::::::::::::::::::::::::::
::  strlen2.5
:strlen2.5  StrVar  [RtnVar]
  setlocal enableDelayedExpansion
  set "s=!%~1!"
  set len=0
  if defined s for /l %%N in (1,1,8192) do if "!s:~%%N,-%%N!" equ "" (
	  set len=%%N
    goto :break
  )
  :break
  set /a len=2*!len!-1
  for %%E in (!len!) do (
	set s=!s:~%%E!
  )
  if "!s!" neq "" set /a len=!len!+1
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b
::::::::::::::::::::::::::::::::::::::

::::::::::::::::::::::::::::::::::::::::
::  strlen2.9
:strlen2.9  StrVar  [RtnVar]
  setlocal enableDelayedExpansion
  set "s=!%~1!"
  set len=0  
  if defined s for /l %%N in (1,5,8192) do if "!s:~%%N,-%%N!" equ "" (
	  set len=%%N
    goto :break
  ) 
  :break
 
  if !len! gtr 1 (
	set /a len=2*!len!-12
	for %%E in (!len!) do (
		set s=!s:~%%E!
	)
  )
  
  if defined s (
    if "!s:~0!" neq "" set /a len=!len!+1
	if "!s:~1!" neq "" set /a len=!len!+1
	if "!s:~2!" neq "" set /a len=!len!+1
	if "!s:~3!" neq "" set /a len=!len!+1
	if "!s:~4!" neq "" set /a len=!len!+1
	if "!s:~5!" neq "" set /a len=!len!+1
	if "!s:~6!" neq "" set /a len=!len!+1
	if "!s:~7!" neq "" set /a len=!len!+1
	if "!s:~8!" neq "" set /a len=!len!+1
	if "!s:~9!" neq "" set /a len=!len!+1
	if "!s:~10!" neq "" set /a len=!len!+1
	if "!s:~11!" neq "" set /a len=!len!+1
  )

  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b
::::::::::::::::::::::::::::::::::::::


::::::::::::::::::::::::::::::::::::::::
::  strlen0.3 
:strlen0.3  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "s=#!%~1!"
  set "len=0"
  for %%A in (2187 729 243 81 27 9 3 1) do (
	set /A mod=2*%%A
	for %%Z in (!mod!) do (
		if "!s:~%%Z,1!" neq "" (
			set /a "len+=%%Z"
			set "s=!s:~%%Z!"
			
		) else (
			if "!s:~%%A,1!" neq "" (
				set /a "len+=%%A"
				set "s=!s:~%%A!"
			)
		)
	)
  )
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo **%len%**
exit /b
::::::::::::::::::::::::::::::::::::::

and the output :

 -----------------
 --- strlen2.9 ---
 -----------------

str5 -> 2    [ 11:21:11.43 - 11:21:11.41 ]
str1 -> 2    [ 11:21:11.46 - 11:21:11.44 ]
str10 -> 2    [ 11:21:11.49 - 11:21:11.47 ]
str50 -> 2    [ 11:21:11.52 - 11:21:11.50 ]
str100 -> 2    [ 11:21:11.55 - 11:21:11.53 ]
str1000 -> 2    [ 11:21:11.58 - 11:21:11.56 ]
str5000 -> 3    [ 11:21:11.62 - 11:21:11.59 ]

 -----------------
 --- strlen1 ---
 -----------------

str5 -> 2    [ 11:21:11.65 - 11:21:11.63 ]
str1 -> 0    [ 11:21:11.66 - 11:21:11.66 ]
str10 -> 3    [ 11:21:11.70 - 11:21:11.67 ]
str50 -> 15    [ 11:21:11.86 - 11:21:11.71 ]
str100 -> 28    [ 11:21:12.15 - 11:21:11.87 ]
str1000 -> 287    [ 11:21:15.03 - 11:21:12.16 ]
str5000 -> 1452    [ 11:21:29.56 - 11:21:15.04 ]

 -----------------
 --- strlen2.5 ---
 -----------------

str5 -> 7    [ 11:21:29.64 - 11:21:29.57 ]
str1 -> 6    [ 11:21:29.71 - 11:21:29.65 ]
str10 -> 7    [ 11:21:29.79 - 11:21:29.72 ]
str50 -> 7    [ 11:21:29.87 - 11:21:29.80 ]
str100 -> 6    [ 11:21:29.94 - 11:21:29.88 ]
str1000 -> 7    [ 11:21:30.02 - 11:21:29.95 ]
str5000 -> 13    [ 11:21:30.16 - 11:21:30.03 ]

 -----------------
 --- strlen2 ---
 -----------------

str5 -> 1    [ 11:21:30.18 - 11:21:30.17 ]
str1 -> 1    [ 11:21:30.20 - 11:21:30.19 ]
str10 -> 0    [ 11:21:30.21 - 11:21:30.21 ]
str50 -> 1    [ 11:21:30.23 - 11:21:30.22 ]
str100 -> 1    [ 11:21:30.25 - 11:21:30.24 ]
str1000 -> 1    [ 11:21:30.27 - 11:21:30.26 ]
str5000 -> 1    [ 11:21:30.29 - 11:21:30.28 ]

 -----------------
 --- strlen3 ---
 -----------------

str5 -> 6    [ 11:21:30.36 - 11:21:30.30 ]
str1 -> 6    [ 11:21:30.42 - 11:21:30.36 ]
str10 -> 4    [ 11:21:30.47 - 11:21:30.43 ]
str50 -> 5    [ 11:21:30.53 - 11:21:30.48 ]
str100 -> 5    [ 11:21:30.59 - 11:21:30.54 ]
str1000 -> 5    [ 11:21:30.65 - 11:21:30.60 ]
str5000 -> 6    [ 11:21:30.72 - 11:21:30.66 ]

 -----------------
 --- strlen0.3 ---
 -----------------

str5 -> 1    [ 11:21:30.75 - 11:21:30.74 ]
str1 -> 1    [ 11:21:30.77 - 11:21:30.76 ]
str10 -> 1    [ 11:21:30.80 - 11:21:30.79 ]
str50 -> 0    [ 11:21:30.82 - 11:21:30.82 ]
str100 -> 1    [ 11:21:30.85 - 11:21:30.84 ]
str1000 -> 1    [ 11:21:30.87 - 11:21:30.86 ]
str5000 -> 1    [ 11:21:30.90 - 11:21:30.89 ]

 -----------------
 --- strlen0 ---
 -----------------

str5 -> 1    [ 11:21:30.93 - 11:21:30.92 ]
str1 -> 1    [ 11:21:30.95 - 11:21:30.94 ]
str10 -> 1    [ 11:21:30.97 - 11:21:30.96 ]
str50 -> 1    [ 11:21:30.99 - 11:21:30.98 ]
str100 -> 0    [ 11:21:31.00 - 11:21:31.00 ]
str1000 -> 1    [ 11:21:31.02 - 11:21:31.01 ]
str5000 -> 0    [ 11:21:31.03 - 11:21:31.03 ]

Offline

#7 25 Jan 2013 12:24

bluesxman
Member
From: UK
Registered: 29 Dec 2006
Posts: 1,029

Re: stringlength.cmd - just created - for anybody who can use it

It's more meaningful to perform a large number iterations and time that.  I found minor fluctuations in system load meant that some runs took 0.01s while others reported 0.05s with the exact same parameters.

Here's my own tunable (if not very successful at larger sizes) attempt and the harness I used to test it (calls a timing script I wrote a couple of years ago, included at the end):

@echo off

setlocal enabledelayedexpansion

call timer.cmd stop >nul

set "string="
set "i=1000"

for %%S in (
0
5
326
8100
) do (
	set "string="
	echo:Begin
	for /l %%C in (1,1,%%S) do set "string=!string!a"
	echo !time! %%S
	call timer.cmd start
	for /l %%a in (1,1,%i%) do (
	call :strLen "!string!" >nul
	)
	call timer.cmd stop
	echo !time! %%S = !length!
	echo:
)

pause

goto :EOF



:strLen
setlocal enabledelayedexpansion

set "blocksize=128"
set "string=%~1"

if not defined string (
	set "length=0"
	goto :end
)

set "upper="
set "lower="

for /l %%O in (0,%blocksize%,8192) do (
	if not defined upper (
		set "tempstring=!string:~%%O!"
		if not defined tempstring (
			set "upper=%%O"
		) else (
			set "lower=%%O"
		)
	)
)
set "length="

for /l %%O in (%lower%,1,%upper%) do (
	if not defined length (
		set "tempstring=!string:~%%O!"
		if not defined tempstring (
			set "length=%%O"
		)
	)
)

:end
endlocal & set "length=%length%"
timer.cmd wrote:
@echo off

setlocal

set time=%~2
set time=%time: =0%

set stamp.file=%temp%\%~n0.stamp

if /i "%~1" EQU "start" call :make.stamp
if /i "%~1" EQU "stop"  call :read.stamp stop
if /i "%~1" EQU "lap"   call :read.stamp lap
if    "%~1" EQU ""      call :status

endlocal

goto :EOF

:status

if exist "%stamp.file%" (
	if /i "%~1" NEQ "/q" echo:Timer is active.
	exit /b 0
)

echo:Timer is not active.

exit /b 1

:make.stamp

if exist "%stamp.file%" call :read.stamp stop

set start.time=%time%

(echo:%start.time%) > "%stamp.file%"

echo:Timer started %start.time%

goto :EOF

:read.stamp

call :status /q

if errorlevel 1 goto :EOF

set stop.time=%time%

set /p start.time=< "%stamp.file%"

echo:Timer started %start.time%
echo:Timer %1ped %stop.time%

if %1 EQU stop del "%stamp.file%"

call :calc.time.code %start.time%
set start.time.code=%errorlevel%

call :calc.time.code %stop.time%
set stop.time.code=%errorlevel%

set /a diff.time.code=stop.time.code - start.time.code

if %diff.time.code% LSS 0 set /a diff.time.code+=(24 * 60 * 60 * 100)

setlocal

set /a hs=diff.time.code %% 100
set /a diff.time.code/=100
set /a ss=diff.time.code %% 60
set /a diff.time.code/=60
set /a mm=diff.time.code %% 60
set /a diff.time.code/=60
set /a hh=diff.time.code

set hh=0%hh%
set mm=0%mm%
set ss=0%ss%
set hs=0%hs%

endlocal & set diff.time=%hh:~-2%:%mm:~-2%:%ss:~-2%.%hs:~-2%

echo %diff.time.code% hundredths of a second
echo %diff.time%

goto :EOF

:calc.time.code

setlocal

for /f "usebackq tokens=1,2,3,4 delims=:." %%a in ('%1') do (
    set hh=%%a
    set mm=%%b
    set ss=%%c
    set hs=%%d
)

set /a hh=((%hh:~0,1% * 10) + %hh:~1,1%) * 60 * 60 * 100
set /a mm=((%mm:~0,1% * 10) + %mm:~1,1%) * 60 * 100
set /a ss=((%ss:~0,1% * 10) + %ss:~1,1%) * 100
set /a hs=((%hs:~0,1% * 10) + %hs:~1,1%)

set /a time.code=hh + mm + ss + hs

endlocal & exit /b %time.code%

Last edited by bluesxman (25 Jan 2013 12:27)


cmd | *sh | Puppet | PowerShell | AutoIT3

Offline

#8 04 Dec 2013 10:27

bearslumber
Member
Registered: 04 Dec 2013
Posts: 5

Re: stringlength.cmd - just created - for anybody who can use it

Hi Guys,

I have tried many of the functions but none of them handle spaces in the string correctly.

The string I am trying to parse is...

"C:\Program Files\Microsoft SQL Server\MSSQL11.SATLOC\MSSQL"

The functions return length of 56, but the length is 59.

I can only assume that the 3 spaces are not being counted.

Any ideas?

Bearslumber

Offline

#9 04 Dec 2013 10:38

foxidrive
Member
Registered: 04 Apr 2013
Posts: 339

Re: stringlength.cmd - just created - for anybody who can use it

That string is 58 characters long, not counting the quotes. smile
Paste it into a file and look at the filesize.

Offline

#10 04 Dec 2013 12:03

bearslumber
Member
Registered: 04 Dec 2013
Posts: 5

Re: stringlength.cmd - just created - for anybody who can use it

@foxdrive

Yes. You are correct.

either way though, none of the functions I tried returned 58. They all return 56.

I also tried the strLen function in DosTips, and that also returns 56.

there is something in common with all the functions. Either that or I am definitely going mad.

Any ideas?

Any help is greatly appreciated.

Bearslumber

Offline

#11 04 Dec 2013 12:12

npocmaka
Member
From: Bulgaria
Registered: 03 Dec 2009
Posts: 421

Re: stringlength.cmd - just created - for anybody who can use it

all of the functions dequote the passed string.So this reduce the length to 56.

Offline

#12 04 Dec 2013 12:44

foxidrive
Member
Registered: 04 Apr 2013
Posts: 339

Re: stringlength.cmd - just created - for anybody who can use it

No npocmaka, the quoted string is 60 characters.

But to Bearslumber, I tried the first routine in this thread and it returns 58 here (with a space)


@echo off
set "abc=C:\Program Files\Microsoft SQL Server\MSSQL11.SATLOC\MSSQL"
call :strlen "%abc%" def
echo "%def%"
pause
goto :EOF

:strlen [%1 -string, %2 variable to store result in]
setlocal
set string=%~1
rem if the string contains double quotes it will harm the IF checks
set string=%string:"=.%
set var=%~2
set /a counter=0
if "%string%" equ "" goto :return
:loop 
	rem counter will hold the 1/2 of the string lenght
	set /a counter=%counter%+1
	set string=%string:~1,-1%
	if "%string%" equ "" (
		goto :endloop
	)

goto :loop
:endloop

set string=%~1
set string=%string:"=.%

set /a counter=2*%counter%-1
rem checking  if the string is with even or with uneven lenght 
call set string=%%string:~%counter%%%
if "%string%" neq "" set /a counter=%counter%+1
echo %counter%
:return
endlocal & set %var%=%counter% >nul  2>&1

Last edited by foxidrive (04 Dec 2013 12:52)

Offline

#13 04 Dec 2013 12:54

npocmaka
Member
From: Bulgaria
Registered: 03 Dec 2009
Posts: 421

Re: stringlength.cmd - just created - for anybody who can use it

@echo off
set "str="C:\Program Files\Microsoft SQL Server\MSSQL11.SATLOC\MSSQL""
call :strlen0.3 str
call :strlen2.5 str
call :strlen4 str
call :strlen0 str

goto :eof

:strlen0.3  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "s=#!%~1!"
  set "len=0"
  for %%A in (2187 729 243 81 27 9 3 1) do (
	set /A mod=2*%%A
	for %%Z in (!mod!) do (
		if "!s:~%%Z,1!" neq "" (
			set /a "len+=%%Z"
			set "s=!s:~%%Z!"
			
		) else (
			if "!s:~%%A,1!" neq "" (
				set /a "len+=%%A"
				set "s=!s:~%%A!"
			)
		)
	)
  )
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo **%len%**
exit /b

:strlen2.5  StrVar  [RtnVar]
  setlocal enableDelayedExpansion
  set "s=!%~1!"
  set len=0
  if defined s for /l %%N in (1,1,8192) do if "!s:~%%N,-%%N!" equ "" (
	  set len=%%N
    goto :break
  )
  :break
  set /a len=2*!len!-1
  for %%E in (!len!) do (
	set s=!s:~%%E!
  )
  if "!s!" neq "" set /a len=!len!+1
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo ~~%len%~~
exit /b

:strlen4  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "tempFile=%temp%\strlen%random%.tmp"
  echo(!%~1!>"%tempFile%"
  for %%F in ("%tempFile%") do set /a len=%%~zF-2
  del "%tempFile%"
  endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

:strlen0  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "s=#!%~1!"
  set "len=0"
  for %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
    if "!s:~%%N,1!" neq "" (
      set /a "len+=%%N"
      set "s=!s:~%%N!"
    )
  )
  endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

output

**60**
~~60~~
60
60

with removed quotes:

**58**
~~58~~
58
58

Offline

#14 04 Dec 2013 21:35

bearslumber
Member
Registered: 04 Dec 2013
Posts: 5

Re: stringlength.cmd - just created - for anybody who can use it

Hi Guys,

Many thanks for your answers.

I attach a code snippet to give you an idea of what I'm trying to do (the context) and under which I get the result of 56. I used npocmaka's strlen0.3.

I'm not sure how to apply the quotes in this situation as implied by the posts, and any help is greatly appreciated.

echo off

setlocal ENABLEEXTENSIONS

set /A LINES_TO_SKIP=4
rem on 64 bit architecture 
wmic os get osarchitecture | findstr /I 64 && set /A LINES_TO_SKIP=2

set KEY_NAME="HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\SATLOC\Setup"
set VALUE_NAME="SQLPath"

FOR /F "usebackq skip=%LINES_TO_SKIP% tokens=2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
    set INSTANCE_PATHTYPE=%%A
    set INSTANCE_PATHVALUE=%%B
)

echo on

echo %INSTANCE_PATHVALUE%

set len=
call:strlen0.3 "%INSTANCE_PATHVALUE%" len

echo %len%

Output:

C:\Program Files\Microsoft SQL Server\MSSQL11.SATLOC\MSSQL

56

Offline

#15 04 Dec 2013 23:53

Simon Sheppard
Super Administrator
Registered: 27 Aug 2005
Posts: 873
Website

Re: stringlength.cmd - just created - for anybody who can use it

I'm thinking if you are going to create a temp file, then why not just let the file system calculate the size/length for you, just subtract 2 to account for the terminating CR/LF

@echo off
setlocal
Set _temp=%temp%\strlen%random%.txt
Echo %1>%_temp%
For /f "tokens=3" %%G in ('dir /-C %_temp% ^| find "File(s)"') Do Set /a _len=%%G-2
Echo %_len%
Del %_temp%

Call it as
strlen.cmd "some string to measure"

Offline

#16 05 Dec 2013 10:06

bearslumber
Member
Registered: 04 Dec 2013
Posts: 5

Re: stringlength.cmd - just created - for anybody who can use it

Thank you Simon,

Your solution does resolve the issue, albeit a deviation.

With respect to the strlen functions, I believe that the functions would be most useful when used in context where a variable is extrapolated in line from another source, for example in my demonstration.

From my naive perspective, it would seem that hard-coding the string and testing the function provides the expected results, where as if I "stringify" (for want of a better word) a variable and use it in context we get a different result, as I believe I have demonstrated.

This to me suggests there is something subtly different in the formatting of the hard-coded string and my "stringification" of the variable.

I would be grateful if someone could point out that subtlety, or point me to some documentation that would explain the difference.

Don't get me wrong because I am impressed with the strlen solutions, and I still believe the strlen functions are the best common solutions to add to a library. I just need to understand how to use the function it in context, and correctly.

Thanks again.

Bearslumber

Offline

#17 05 Dec 2013 12:07

foxidrive
Member
Registered: 04 Apr 2013
Posts: 339

Re: stringlength.cmd - just created - for anybody who can use it

There are two ways to quote a variable, when you need to protect characters like & for example, or prevent trailing spaces.

This will work and leave the variable contents without surrounding quotes
set "variable=one & two"


This will work and leave the variable contents with surrounding quotes
set variable="one & two"


This will fail when just setting the variable, as is why the above need to be used:
set variable=one & two


It would seem that the way you passed the variable was fubar - but it's hard to see how it would report *less* characters than there are.

Offline

#18 05 Dec 2013 13:04

bearslumber
Member
Registered: 04 Dec 2013
Posts: 5

Re: stringlength.cmd - just created - for anybody who can use it

Thanks for enlightening me foxdrive, much appreciated.

I get it now. The key to the issue is the fubar! with an emphasis on the "FU"!!!

I needed to pass the variable without the parenthesis.

i.e.

call:strlen0.3 INSTANCE_PATHVALUE len

and not

call:strlen0.3 %INSTANCE_PATHVALUE% len

Doh!!!

Offline

#19 17 Jun 2016 18:12

Simon Sheppard
Super Administrator
Registered: 27 Aug 2005
Posts: 873
Website

Re: stringlength.cmd - just created - for anybody who can use it

Over on the main site I have added this page
http://ss64.com/nt/syntax-strlen.html

This example is intended to be simple and easy to follow rather than particularly high performance with long strings. It is probably most similar to StrLen2 that Dave Benham posted above.
Theres also a link back to this thread.

Offline

#20 20 Jun 2016 07:57

jeb
Member
From: Germany
Registered: 19 Nov 2010
Posts: 106

Re: stringlength.cmd - just created - for anybody who can use it

Hi Simon,

nice idea to add the strlen function to the site. smile
I can understand that you take a simple implementation, but ....
Your version fails in too many situaltions.
All special characters breaks your code.

strlen.cmd "Cat&Dog"
strlen.cmd 1^>2

And I can't see why you should use any replace operations.

I would change the code to

Echo off
 Setlocal EnableDelayedExpansion
 Set "_str=%*"
  
 :: Test if empty
 if not defined _str Echo String Length = 0 & ENDLOCAL & set _strlen=0&goto:eof
 
 For /l %%g in (0,1,8191) do (
  REM extract one character
  Set "_char=!_str:~%%g,1!"
  REM if _char is empty we are at the end of the string
  if not defined _char Echo String Length = %%g & ENDLOCAL & set _strlen=%%g& goto:eof
 )

Last edited by jeb (20 Jun 2016 07:58)

Offline

#21 29 Jun 2016 17:48

Simon Sheppard
Super Administrator
Registered: 27 Aug 2005
Posts: 873
Website

Re: stringlength.cmd - just created - for anybody who can use it

^ Thanks Jeb that example is better, interestingly your version is similar to the strlen function in the Batchography book which I have just been reading.
I left in the removal of quotes just so that the script/function can be called passing a string with or without surrounding quotes. Also I mentioned the special characters and linked back to this thread.

Offline

#22 23 Sep 2016 12:26

Tricksy
Member
Registered: 23 Sep 2016
Posts: 10

Re: stringlength.cmd - just created - for anybody who can use it

Hi! I'm new to the forum.  I'm ready for this code:

@ECHO OFF
SETLOCAL
SET String=%*
SET Text=%*
SET Count=1
ECHO Start count:
ECHO.
:MAIN
CALL SET Text=%%String:~,%Count%%%
IF %Count% LEQ 9 (ECHO 0%Count%:%Text%) ELSE (ECHO %Count%:%Text%)
IF "%Text%"=="%String%" GOTO RESULT
SET /A Count=%Count%+1
GOTO MAIN
:RESULT
ENDLOCAL & SET /A Count=%Count%
ECHO.
ECHO String length = %Count%
PAUSE > NUL

Offline

Board footer

Powered by FluxBB