如何在批处理文件中获取字符串长度?

时间:2011-04-29 21:08:17

标签: windows string batch-file

似乎没有一种简单的方法可以在批处理文件中获取字符串的长度。如,

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

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

如果字符串长度函数处理字符串中包含转义字符的所有可能字符,则加分:!%^^()^!

18 个答案:

答案 0 :(得分:79)

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

@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-loops的简单strlen函数 它处理所有字符。

答案 1 :(得分:29)

您可以通过将字符串写入文件然后获取文件的长度,在两行中完全在批处理文件中完成。你只需要减去两个字节来考虑到最后添加的自动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

答案 2 :(得分:20)

我更喜欢jeb's accepted answer - 它是最快的已知解决方案,也是我在自己的脚本中使用的解决方案。 (实际上还有一些关于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's answer作为batch "macro" with arguments。这是一种在DosTips中开发的高级批处理技术,它消除了CALLing a:子例程的固有缓慢过程。您可以获得more background on the concepts behind batch macros here,但该链接使用更原始,不太理想的语法。

下面是一个优化的@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

答案 3 :(得分:8)

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

当然,这在jeb提供的“13 loop”版本中效率不高。但它更容易理解,你的3GHz计算机可以在几分之一秒内完成几千次迭代。

答案 4 :(得分:5)

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

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

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

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

使用for循环捕获结果(或完全使用vbscript作为脚本任务)

当然需要使用批处理创建繁琐的变通方法,并且没有理由不使用它,因为vbscript可用于每个Windows发行版(以及稍后的PowerShell)。

答案 5 :(得分:3)

如果您使用的是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,这对我有用。

答案 6 :(得分:3)

刚刚找到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

它处理转义字符,比上面的大多数解决方案简单一个数量级,并且不包含循环,幻数,DelayedExpansion,临时文件等。

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

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

答案 7 :(得分:2)

我喜欢jmh_gr的two line approach

除非在重定向之前将()放在命令部分周围,否则它不适用于单个数字。由于1>是一个特殊命令,“Echo is On”将被重定向到文件。

此示例应该处理单个数字,但不能处理字符串中可能包含的<等其他特殊字符。

(ECHO %strvar%)> tempfile.txt

答案 8 :(得分:1)

@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设置为您想要查找其长度的任何值,或将其更改为设置/ p var =以便用户输入它。 把它放在这里供将来参考。

答案 9 :(得分:1)

如果您坚持要在纯批处理中具有这个琐碎的功能,我建议这样做:

@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%

PS。您必须启用延迟变量扩展才能运行此功能。

编辑。现在,我做了一个改进的版本:

@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。这也可以通过批处理脚本即时完成。即使您不知道自己是否有编译器,也不知道它在哪里,也可以在脚本中搜索它。

答案 10 :(得分:1)

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

答案 11 :(得分:0)

我想通过说我不太了解编写代码/脚本/等来预先说明这一点。但我想我会分享一个我似乎想出来的解决方案。这里的大部分回复都有点过头了,所以我很想知道我所写的内容是否具有可比性。

@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

答案 12 :(得分:0)

@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 ########################

答案 13 :(得分:0)

这是非常简单

纯批处理溶液。没有临时文件。没有长脚本。

@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 :(得分:0)

我做了这个简单的函数来求字符串的长度

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

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

答案 15 :(得分:-1)

@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

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

答案 16 :(得分:-1)

@echo off
title String Lenght
set /p str=String: 
if exist "%tmp%\STR" (del "%tmp%\STR" /q)
echo %str% > "%tmp%\STR"
cd /d "%tmp%"
for %%P in (STR) do (set STRsize=%%~zP)
set /a result=%STRsize%-3
echo String lenght: %result%
pause>nul
exit /B

用于检测字符串长度的代码。

答案 17 :(得分:-3)

@echo off
title String Lenght
set /p str=String: 
if exist "%tmp%\STR" (del "%tmp%\STR" /q)
echo %str% > "%tmp%\STR"
cd /d "%tmp%"
for %%P in (STR) do (set STRsize=%%~zP)
set /a result=%STRsize%-3
echo String lenght: %result%
pause>nul
exit /B