Я предпочитаю Принятый ответ Джеба - это самое быстрое из известных решений, которое я использую в своих сценариях.(На самом деле в DosTips есть несколько дополнительных оптимизаций, но я не думаю, что они того стоят)
Но интересно создавать новые эффективные алгоритмы.Вот новый алгоритм, который использует опцию FINDSTR / O:
@echo off
setlocal
set "test=Hello world!"
:: Echo the length of TEST
call :strLen test
:: Store the length of TEST in LEN
call :strLen test len
echo len=%len%
exit /b
:strLen 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
Код вычитает 3, потому что анализатор выполняет жонглирование команды и добавляет пробел перед тем, как CMD / V / C ее выполнит.Это можно предотвратить, используя (echo(!%~1!^^^)
.
Для тех, кто хочет максимально возможной производительности, ответ Джеба может быть принят для использования в качестве макроса batch ""с аргументами .Это продвинутая пакетная техника, разработанная в DosTips, которая устраняет медленный процесс вызова C: подпрограммы.Вы можете получить дополнительную информацию о концепциях пакетных макросов здесь , но эта ссылка использует более примитивный, менее желательный синтаксис.
Ниже приведен оптимизированный макрос @strLen, примеры которого показывают различия междумакрос и использование подпрограмм, а также различия в производительности.
@echo off
setlocal disableDelayedExpansion
:: -------- Begin macro definitions ----------
set ^"LF=^
%= This creates a variable containing a single linefeed (0x0A) character =%
^"
:: Define %\n% to effectively issue a newline with line continuation
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
:: @strLen StrVar [RtnVar]
::
:: Computes the length of string in variable StrVar
:: and stores the result in variable RtnVar.
:: If RtnVar is is not specified, then prints the length to stdout.
::
set @strLen=for %%. in (1 2) do if %%.==2 (%\n%
for /f "tokens=1,2 delims=, " %%1 in ("!argv!") do ( endlocal%\n%
set "s=A!%%~1!"%\n%
set "len=0"%\n%
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (%\n%
if "!s:~%%P,1!" neq "" (%\n%
set /a "len+=%%P"%\n%
set "s=!s:~%%P!"%\n%
)%\n%
)%\n%
for %%V in (!len!) do endlocal^&if "%%~2" neq "" (set "%%~2=%%V") else echo %%V%\n%
)%\n%
) else setlocal enableDelayedExpansion^&setlocal^&set argv=,
:: -------- End macro definitions ----------
:: Print out definition of macro
set @strLen
:: Demonstrate usage
set "testString=this has a length of 23"
echo(
echo Testing %%@strLen%% testString
%@strLen% testString
echo(
echo Testing call :strLen testString
call :strLen testString
echo(
echo Testing %%@strLen%% testString rtn
set "rtn="
%@strLen% testString rtn
echo rtn=%rtn%
echo(
echo Testing call :strLen testString rtn
set "rtn="
call :strLen testString rtn
echo rtn=%rtn%
echo(
echo Measuring %%@strLen%% time:
set "t0=%time%"
for /l %%N in (1 1 1000) do %@strlen% testString testLength
set "t1=%time%"
call :printTime
echo(
echo Measuring CALL :strLen time:
set "t0=%time%"
for /l %%N in (1 1 1000) do call :strLen testString testLength
set "t1=%time%"
call :printTime
exit /b
:strlen StrVar [RtnVar]
::
:: Computes the length of string in variable StrVar
:: and stores the result in variable RtnVar.
:: If RtnVar is is not specified, then prints the length to stdout.
::
(
setlocal EnableDelayedExpansion
set "s=A!%~1!"
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
if "%~2" equ "" (echo %len%) else set "%~2=%len%"
exit /b
)
:printTime
setlocal
for /f "tokens=1-4 delims=:.," %%a in ("%t0: =0%") do set /a "t0=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100
for /f "tokens=1-4 delims=:.," %%a in ("%t1: =0%") do set /a "t1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100
set /a tm=t1-t0
if %tm% lss 0 set /a tm+=24*60*60*100
echo %tm:~0,-2%.%tm:~-2% msec
exit /b
- Пример вывода -
@strLen=for %. in (1 2) do if %.==2 (
for /f "tokens=1,2 delims=, " %1 in ("!argv!") do ( endlocal
set "s=A!%~1!"
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!"
)
)
for %V in (!len!) do endlocal&if "%~2" neq "" (set "%~2=%V") else echo %V
)
) else setlocal enableDelayedExpansion&setlocal&set argv=,
Testing %@strLen% testString
23
Testing call :strLen testString
23
Testing %@strLen% testString rtn
rtn=23
Testing call :strLen testString rtn
rtn=23
Measuring %@strLen% time:
1.93 msec
Measuring CALL :strLen time:
7.08 msec