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

ind*_*div 63 windows string batch-file

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

SET MY_STRING=abcdefg
SET /A MY_STRING_LEN=???
Run Code Online (Sandbox Code Playgroud)

我怎样才能找到字符串的长度MY_STRING

如果字符串长度函数处理包括转义字符在内的字符串中的所有可能字符,则奖励积分如下:!%^^()^!.

jeb*_*jeb 83

由于字符串长度没有内置函数,您可以编写自己的函数,如下所示:

@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
)
Run Code Online (Sandbox Code Playgroud)

这个函数总是需要13个循环,而不是需要strlen循环的简单strlen函数.
它处理所有角色.


Jos*_*nig 32

您可以通过将字符串写入文件然后获取文件的长度,在两行中完全在批处理文件中完成.您只需要减去两个字节来考虑添加到结尾的自动CR + LF.

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

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

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

稍微详细一点:

  • FOR %%? IN (filename) DO ( ... :获取有关文件的信息
  • SET /A [variable]=[expression] :用数字方式评估表达式
  • %%~z? :获取文件长度的特殊表达式

将整个命令混为一行:

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

  • 我不想成为仇恨者,但删除完成后创建的临时文件会很不错.这是临时文件xD上的重点.`del tempfile.txt` (3认同)
  • 您应该将其更改为 `&gt;x ECHO.%strvar%` 而不是 `ECHO %strvar%&gt;x`,否则它会在输入如 `2` 或 `3` 时失败 (2认同)
  • 这种方法在概念上极其简单,也是已知最快的方法之一。但我不将它用作通用批处理例程,因为理想情况下,该例程应保证临时文件的完整路径在所有进程中都是唯一的 - 这不是一项微不足道的任务。您不希望例程失败,因为两个进程同时尝试使用相同的文件名。我也同意应在完成后删除临时文件。 (2认同)
  • 这段代码有几个问题。首先使用 `%strvar%` 不好。为什么?尝试使用`set strvar=2` 或`set "strvar=Redirection operators are &lt; and &gt; and &gt;&gt; and |"` 的代码。接下来创建文件比在内存中执行所有操作都慢,并且对 SSD 磁盘不利。如果当前工作目录对于使用此批处理代码的用户帐户进行写保护,则结果也是错误的,因为无法在当前目录中创建文件。如果当前目录中存在`tempfile.txt`,但设置了只读属性,则结果也是错误的。 (2认同)

dbe*_*ham 23

我更喜欢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
Run Code Online (Sandbox Code Playgroud)

代码减去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
Run Code Online (Sandbox Code Playgroud)

- 样品输出 -

@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
Run Code Online (Sandbox Code Playgroud)

  • K + 1是一个有趣的方法.我会添加'skip = 1'和'echo(!%〜1!'.你需要减去3,因为echo在这里总是在每行的末尾添加一个空格(由管道引起?) (2认同)
  • @Jay - 我使用变量而不是字符串文字的原因是因为有许多文字字符串不能作为参数传递,但变量可以包含所有可能的批处理字符串. (2认同)

Cod*_*nes 10

前几行只是为了演示: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
Run Code Online (Sandbox Code Playgroud)

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


gho*_*g74 5

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

WScript.Echo Len( WScript.Arguments(0) )
Run Code Online (Sandbox Code Playgroud)

将其保存为strlen.vbs命令行

c:\test> cscript //nologo strlen.vbs "abcd"
Run Code Online (Sandbox Code Playgroud)

使用for循环捕获结果(或完全使用vbscript进行脚本编写任务)

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

  • 如果你在谈论安全问题,那么`cmd.exe`也可以. (4认同)
  • 有一个很好的理由使用jeb的优化批处理解决方案而不是VBS解决方案.由于初始化VBS的开销,VBS解决方案慢了近3倍.但我同意VBS解决方案很诱人. (3认同)
  • 不过,WSH 可以通过组策略来锁定。 (2认同)

Ale*_*der 5

刚刚找到的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%
Run Code Online (Sandbox Code Playgroud)

输出为:

string "abcdef!%^^()^!" length = 14
Run Code Online (Sandbox Code Playgroud)

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

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

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