Как получить длину строки в пакетном файле?
кажется, что нет простого способа получить длину строки в пакетном файле. Например,
SET MY_STRING=abcdefg
SET /A MY_STRING_LEN=???
как бы я нашел длину строки MY_STRING
?
бонусные очки, если функция длины строки обрабатывает все возможные символы в строках, включая escape-символы, например:!%^^()^!
.
12 ответов:
Как нет встроенной функции для длины строки, вы можете написать свою функцию вот так:
@echo off setlocal set "myString=abcdef!%%^^()^!" call :strlen result myString echo %result% goto :eof :strlen <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 )
эта функция всегда нуждается в 13 петлях, вместо простой функции strlen, которая нуждается в strlen-петлях.
Он обрабатывает все символы.
вы можете сделать это в двух строках, полностью в пакетном файле, записав строку в файл, а затем получить длину файла. Вам просто нужно вычесть два байта, чтобы учесть автоматический 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
предпочитаю джеб принял ответ - это самое быстрое известное решение и тот, который я использую в своих собственных сценариях. (На самом деле есть несколько дополнительных оптимизаций, связанных с 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!^^^)
.
для тех, кто хочет абсолютную максимальную производительность возможно,джеба может быть принят для использования в качестве пакетный "макрос" с аргументами. Это усовершенствованный пакетный метод, разработанный в DosTips, который устраняет изначально медленный процесс вызова подпрограммы:. Вы можете получить подробнее о концепциях пакетных макросов здесь, но эта ссылка использует больше примитивный, менее желательный синтаксис.
Ниже приведен оптимизированный макрос @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
первые несколько строк просто демонстрируют функцию: 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 может проскользнуть через несколько тысяч итераций за небольшую долю секунды.
Да, конечно, есть простой способ, используя vbscript (или powershell).
WScript.Echo Len( WScript.Arguments(0) )
сохранить как
strlen.vbs
и в командной строкеc:\test> cscript //nologo strlen.vbs "abcd"
использовать для петли, чтобы захватить результат ( или VBScript используется исключительно для задания сценариев)
конечно, лучше создавать громоздкие обходные пути с помощью пакета, и нет никаких оправданий, чтобы не использовать его, так как vbscript доступен с каждым дистрибутивом Windows ( и powershell позже).
Если вы находитесь на 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 и это работает для меня.
мне нравится две линии подхода из jmh_gr.
Он не будет работать с одноразрядными числами, если вы не поставите
()
вокруг части команды перед перенаправлением. так как1>
специальная команда "Echo Is On" будет перенаправлена в файл.этот пример должен заботиться о однозначных числах, но не о других специальных символах, таких как
<
что может быть в строке.(ECHO %strvar%)> tempfile.txt
только что нашел окончательное решение:
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, временных файлов и т. д.
в случае использования вне пакетного сценария (имеется в виду ввод команд в консоль вручную), замените
%%RESULT%%
ключ%RESULT%
.при необходимости
%ERRORLEVEL%
переменная может быть установлена вFALSE
С помощью любой команды NOP, например,echo. >nul
просто еще один пакетный скрипт для вычисления длины строки, всего за несколько строк. Это может быть не самый быстрый, но он довольно маленький. Подпрограмма": 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
@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= так, чтобы пользователь вводил его. Поместите это здесь для дальнейшего использования.
@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 ########################
Я хочу предварить это, сказав, что я мало знаю о написании кода/скрипта/и т. д. но я подумал, что поделюсь решением, которое я, кажется, придумал. Большинство ответов здесь как бы прошли через мою голову, поэтому мне было любопытно узнать, сопоставимо ли то, что я написал.
@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