How do you get the string length in a batch file?

似乎没有一种简单的方法来获取批处理文件中字符串的长度。例如:

SET MY_STRING=abcdefg
SET /A MY_STRING_LEN=???

如何找到 MY_STRING的字符串长度?

如果字符串长度函数处理包括转义字符在内的字符串中的所有可能字符,则得到额外的分数,如下所示: !%^^()^!

188646 次浏览

由于没有内置的字符串长度函数,您可以像下面这样编写自己的函数:

@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-loop。
它处理所有字符。

奇怪的表达式 (set^ tmp=!%~2!)是处理超长字符串所必需的,否则就不可能复制它们。

是的,当然有一个简单的方法,使用 vbscript (或 powershell)。

WScript.Echo Len( WScript.Arguments(0) )

将其保存为 strlen.vbs并在命令行上

c:\test> cscript //nologo strlen.vbs "abcd"

Use a for loop to capture the result ( or use vbscript entirely for your scripting task)

当然比使用批处理创建繁琐的工作区要好,而且没有理由不使用它,因为每个 Windows 发行版都提供 vbscript (以及后面的 powershell)。

通过将字符串写入文件,然后获取文件的长度,您可以在两行中完全在批处理文件中完成此操作。您只需要减去两个字节,就可以计算添加到末尾的自动 CR + LF。

假设您的字符串位于一个名为 strvar的变量中:

ECHO %strvar%> tempfile.txt
FOR %%? IN (tempfile.txt) DO ( SET /A strlength=%%~z? - 2 )

字符串的长度现在位于一个名为 strlength的变量中。

更详细地说:

  • 获取有关文件的信息
  • SET /A [variable]=[expression]: 数值计算表达式
  • %%~z?: 获取文件长度的特殊表达式

将整个命令混合在一行中:

ECHO %strvar%>x&FOR %%? IN (x) DO SET /A strlength=%%~z? - 2&del x

我喜欢 jmh _ gr 的 两线进场

除非在重定向之前将 ()放在命令部分的周围,否则它不能处理单位数字。因为 1>是一个特殊的命令“ Echo is On”将被重定向到该文件。

这个示例应该处理单位数字,而不是字符串中可能包含的其他特殊字符,如 <

(ECHO %strvar%)> tempfile.txt

我更喜欢 Jeb 接受了这个答案-它是最快的已知解决方案,我在我自己的脚本中使用的一个。(实际上,在 DosTips 上还有一些额外的优化,但我认为它们不值得)

But it is fun to come up with new efficient algorithms. Here is a new algorithm that uses the FINDSTR /O option:

@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 的回答作为 batch "macro" with arguments。这是 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

前几行只是演示: 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

Of course, this is not quite as efficient at the "13 loop" version provided by jeb. But it is easier to understand, and your 3GHz computer can slip through a few thousand iterations in a small fraction of a second.

如果你使用的是 WindowsVista + ,那么试试这个 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

我用的是 Windows7x64这对我很有用。

只是另一个用于计算字符串长度的批处理脚本,只需要几行代码。它可能不是最快的,但它很小。子例程“ : 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
::   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 & 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


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

终极解决方案:

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

它处理转义字符,比上面大多数解决方案的数量级更简单,并且不包含循环、魔术数字、延迟/扩展、临时文件等。

如果在批处理脚本之外使用(意味着手动将命令放到控制台) ,则将 %%RESULT%%键替换为 %RESULT%

如果需要,可以使用任何 NOP 命令将 %ERRORLEVEL%变量设置为 FALSE,例如 echo. >nul

It's 简单多了!

纯批处理解决方案。没有临时文件。没有长脚本。

@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 is the maximum estimated string lenght. Change it based on your needs.

@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

这对我有用。希望这对别人有用。不需要调整任何长度。我只需删除第一个字母,然后与“直到字符串等于”进行比较。

如果你坚持在纯批处理中使用这种琐碎的函数,我建议如下:

@echo off


set x=somestring
set n=0
set m=255


:loop
if "!x:~%m%,1!" == "" (
set /a "m>>=1"
goto loop
) else (
set /a n+=%m%+1
set x=!x:~%m%!
set x=!x:~1!
if not "!x!" == "" goto loop
)


echo %n%

另外,您必须延迟变量扩展才能运行此命令。

现在我做了一个改进版本:

@echo off


set x=somestring
set n=0


for %%m in (4095 2047 1023 511 255 127 63 31 15 7 3 1 0) do (
if not "!x:~%%m,1!" == "" (
set /a n+=%%m+1
set x=!x:~%%m!
set x=!x:~1!
if "!x!" == "" goto done
)
)


:done
echo %n%

编辑2。如果你的系统上有一个 C 编译器或者其他什么东西,你可以创建你需要的程序,如果它们不存在,你可以马上错过它们。这种方法非常普遍。以字符串长度为例:

@echo off


set x=somestring


if exist strlen.exe goto comp
echo #include "string.h" > strlen.c
echo int main(int argc, char* argv[]) { return strlen(argv[1]); } >> strlen.c
CL strlen.c


:comp
strlen "%x%"
set n=%errorlevel%
echo %n%

您必须适当地设置 PATH、 INCLUDE 和 LIB。这也可以从批处理脚本动态执行。即使你不知道你是否有一个编译器或不知道它在哪里,你可以在你的脚本中搜索它。

您可以尝试此代码。快速,您还可以包括特殊字符

@echo off
set "str=[string]"
echo %str% > "%tmp%\STR"
for %%P in ("%TMP%\STR") do (set /a strlen=%%~zP-3)
echo String lenght: %strlen%

I made this simple function to find the length of string

for /l %%a in (0,1,10000) do if not "!text:~%%a,1!" == "" set/a text_len=!text_len!+1

您需要确保首先定义 !text!!text_len!变量

我知道现在有点晚了——比如10年,但是我所做的是编写一个名为 strlen 的 C # Console 应用程序来调用它,然后使用错误级别来获取返回的长度