Как получить длину строки в пакетном файле?
Кажется, что нет простого способа получить длину строки в пакетном файле. Например.
SET MY_STRING=abcdefg
SET /A MY_STRING_LEN=???
Как бы найти длину строки MY_STRING
?
Бонусные точки, если функция длины строки обрабатывает все возможные символы в строках, включая escape-символы, например: !%^^()^!
.
Ответы
Ответ 1
Поскольку встроенной функции для длины строки не существует, вы можете написать свою собственную функцию, например, такую:
@echo off
setlocal
REM *** Some tests, to check the functionality ***
REM *** An emptyStr has the length 0
set "emptyString="
call :strlen result emptyString
echo %result%
REM *** This string has the length 14
set "myString=abcdef!%%^^()^!"
call :strlen result myString
echo %result%
REM *** This string has the maximum length of 8191
setlocal EnableDelayedExpansion
set "long=."
FOR /L %%n in (1 1 13) DO set "long=!long:~-4000!!long:~-4000!"
(set^ longString=!long!!long:~-191!)
call :strlen result longString
echo %result%
goto :eof
REM ********* function *****************************
:strlen <resultVar> <stringVar>
(
setlocal EnableDelayedExpansion
(set^ tmp=!%~2!)
if defined tmp (
set "len=1"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!tmp:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "tmp=!tmp:~%%P!"
)
)
) ELSE (
set len=0
)
)
(
endlocal
set "%~1=%len%"
exit /b
)
Для этой функции всегда требуется 13 циклов вместо простой функции strlen, для которой нужны циклы strlen.
Он обрабатывает все символы.
Странное выражение (set^ tmp=!%~2!)
необходимо для обработки сверхдлинных строк, иначе их невозможно скопировать.
Ответ 2
Вы можете сделать это в двух строках, полностью в пакетном файле, путем записи строки в файл, а затем получения длины файла. Вам просто нужно вычесть два байта для учета автоматического добавления CR + LF в конец.
Скажем, ваша строка находится в переменной под названием strvar
:
ECHO %strvar%> tempfile.txt
FOR %%? IN (tempfile.txt) DO ( SET /A strlength=%%~z? - 2 )
Длина строки теперь находится в переменной с именем strlength
.
Немного подробнее:
-
FOR %%? IN (filename) DO ( ...
: получает информацию о файле
-
SET /A [variable]=[expression]
: вычислить выражение численно
-
%%~z?
: специальное выражение для получения длины файла
Чтобы спеть всю команду в одной строке:
ECHO %strvar%>x&FOR %%? IN (x) DO SET /A strlength=%%~z? - 2&del x
Ответ 3
Я предпочитаю, чтобы принятый ответ jeb - это самое быстрое решение и тот, который я использую в своих собственных сценариях. (На самом деле в 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!^^^)
.
Для тех, кто хочет получить максимальную максимальную производительность, ответ jeb может быть принят для использования в качестве пакетного "макроса" с аргументами. Это усовершенствованная пакетная техника, разработанная на DosTips, которая устраняет по сути медленный процесс подпрограммы CALLing a:. Вы можете получить больше информации о концепциях пакетных макросов здесь, но эта ссылка использует более примитивный, менее желательный синтаксис.
Ниже приведен оптимизированный макрос @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
Ответ 4
Первые несколько строк просто демонстрируют функцию: strLen.
@echo off
set "strToMeasure=This is a string"
call :strLen strToMeasure strlen
echo.String is %strlen% characters long
exit /b
:strLen
setlocal enabledelayedexpansion
:strLen_Loop
if not "!%1:~%len%!"=="" set /A len+=1 & goto :strLen_Loop
(endlocal & set %2=%len%)
goto :eof
Конечно, это не так эффективно в версии "13 loop", предоставляемой jeb. Но это легче понять, и ваш 3GHz-компьютер может пропустить несколько тысяч итераций за небольшую долю секунды.
Ответ 5
Да, конечно, есть простой способ, используя vbscript (или powershell).
WScript.Echo Len( WScript.Arguments(0) )
сохраните это как strlen.vbs
и в командной строке
c:\test> cscript //nologo strlen.vbs "abcd"
Используйте цикл for для захвата результата (или используйте vbscript полностью для вашей задачи сценариев)
Конечно, нужно создавать громоздкие обходные пути, используя пакет, и нет никаких оправданий, чтобы не использовать его, поскольку vbscript доступен с каждым дистрибутивом Windows (и powershell в дальнейшем).
Ответ 6
Только что нашел решение ULTIMATE:
set "MYSTRING=abcdef!%%^^()^!"
(echo "%MYSTRING%" & echo.) | findstr /O . | more +1 | (set /P RESULT= & call exit /B %%RESULT%%)
set /A STRLENGTH=%ERRORLEVEL%-5
echo string "%MYSTRING%" length = %STRLENGTH%
Вывод:
string "abcdef!%^^()^!" length = 14
Он обрабатывает escape-символы, на порядок проще, чем большинство решений выше, и не содержит циклов, магических чисел, файлов DelayedExpansion, temp и т.д.
В случае использования вне пакета script (например, для ввода команд вручную), замените %%RESULT%%
на %RESULT%
.
При необходимости переменная %ERRORLEVEL%
может быть установлена на FALSE
с использованием любой команды NOP, например. echo. >nul
Ответ 7
Если вы используете Windows Vista +, попробуйте этот метод Powershell:
For /F %%L in ('Powershell $Env:MY_STRING.Length') do (
Set MY_STRING_LEN=%%L
)
или, альтернативно:
Powershell $Env:MY_STRING.Length > %Temp%\TmpFile.txt
Set /p MY_STRING_LEN = < %Temp%\TmpFile.txt
Del %Temp%\TmpFile.txt
Я нахожусь в Windows 7 x64, и это работает для меня.
Ответ 8
Мне нравится двухстрочный подход jmh_gr.
Он не будет работать с однозначными числами, если вы не поместите ()
вокруг части команды перед перенаправлением. поскольку 1>
- специальная команда "Echo is On" будет перенаправлена в файл.
В этом примере должны учитываться одиночные цифры, но не другие специальные символы, такие как <
, которые могут быть в строке.
(ECHO %strvar%)> tempfile.txt
Ответ 9
Еще одна партия script для вычисления длины строки всего несколькими строками. Это может быть не самый быстрый, но он довольно маленький. Подпрограмма ": len" возвращает длину во втором параметре. Первый параметр - фактическая строка, анализируемая.
Обратите внимание: специальные символы должны быть экранированы, то есть в случае любой строки в пакетном файле.
@echo off
setlocal
call :len "Sample text" a
echo The string has %a% characters.
endlocal
goto :eof
:len <string> <length_variable> - note: string must be quoted because it may have spaces
setlocal enabledelayedexpansion&set l=0&set str=%~1
:len_loop
set x=!str:~%l%,1!&if not defined x (endlocal&set "%~2=%l%"&goto :eof)
set /a l=%l%+1&goto :len_loop
Ответ 10
@echo off & setlocal EnableDelayedExpansion
set Var=finding the length of strings
for /l %%A in (0,1,10000) do if not "%Var%"=="!Var:~0,%%A!" (set /a Length+=1) else (echo !Length! & pause & exit /b)
установите var так, как вы хотите найти его длину, или измените его на set/p var =, чтобы пользователь ввел его.
Поместите это здесь для справок в будущем.
Ответ 11
@echo off
:: warning doesn't like * ( in mystring
setlocal enabledelayedexpansion
set mystring=this is my string to be counted forty one
call :getsize %mystring%
echo count=%count% of "%mystring%"
set mystring=this is my string to be counted
call :getsize %mystring%
echo count=%count% of "%mystring%"
set mystring=this is my string
call :getsize %mystring%
echo count=%count% of "%mystring%"
echo.
pause
goto :eof
:: Get length of mystring line ######### subroutine getsize ########
:getsize
set count=0
for /l %%n in (0,1,2000) do (
set chars=
set chars=!mystring:~%%n!
if defined chars set /a count+=1
)
goto :eof
:: ############## end of subroutine getsize ########################
Ответ 12
Я хочу предисловие к этому, сказав, что я мало знаю о написании кода/ script/и т.д. но думал, что я поделюсь решением, которое, похоже, придумал. Большинство ответов здесь вроде бы перевернули мою голову, поэтому мне было любопытно узнать, сопоставимо ли то, что я написал.
@echo off
set stringLength=0
call:stringEater "It counts most characters"
echo %stringLength%
echo.&pause&goto:eof
:stringEater
set var=%~1
:subString
set n=%var:~0,1%
if "%n%"=="" (
goto:eof
) else if "%n%"==" " (
set /a stringLength=%stringLength%+1
) else (
set /a stringLength=%stringLength%+1
)
set var=%var:~1,1000%
if "%var%"=="" (
goto:eof
) else (
goto subString
)
goto:eof
Ответ 13
Это просто Simplier !
Чистое пакетное решение. Нет временных файлов. Нет длинных скриптов.
@echo off
setlocal enabledelayedexpansion
set String=abcde12345
for /L %%x in (1,1,1000) do ( if "!String:~%%x!"=="" set Lenght=%%x & goto Result )
:Result
echo Lenght: !Lenght!
1000
- максимальная длина строки. Измените его на основе ваших потребностей.
Ответ 14
@ECHO OFF
SET string=
SET /A stringLength=0
:CheckNextLetter
REM Subtract the first letter and count up until the string="".
IF "%string%" NEQ "" (
SET string=%string:~1%
SET /A stringLength=%stringLength%+1
GOTO :CheckNextLetter
) ELSE (
GOTO :TheEnd
)
:TheEnd
ECHO There is %stringLength% character^(s^) in the string.
PAUSE
Это работает для меня. Надеюсь, что это полезно для кого-то еще. Не нужно настраивать на любую длину. Я просто удаляю первую букву и сравниваю с "", пока строка не станет равной "".