web-dev-qa-db-ja.com

バッチファイルで文字列の長さを取得するにはどうすればよいですか?

バッチファイルで文字列の長さを取得する簡単な方法はないようです。例えば。、

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

MY_STRINGの文字列の長さを見つけるにはどうすればよいですか?

!%^^()^!のように、文字列長関数がエスケープ文字を含む文字列内のすべての可能な文字を処理する場合のボーナスポイント。

62
indiv

文字列の長さのための組み込み関数がないため、次のような独自の関数を作成できます。

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

この関数は、strlenループを必要とする単純なstrlen関数の代わりに、常に13のループを必要とします。
すべての文字を処理します。

奇妙な式(set^ tmp=!%~2!)は、非常に長い文字列を処理するために必要です。そうでない場合、それらをコピーすることはできません。

82
jeb

文字列をファイルに書き込み、ファイルの長さを取得することにより、完全にバッチファイルで2行で実行できます。最後に追加された自動CR + LFを考慮して、2バイトを差し引くだけです。

文字列が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?:ファイルの長さを取得するための特別な式

コマンド全体を1行でマッシュするには:

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

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

パーサーがコマンドを処理し、CMD/V/Cが実行する前にスペースを追加するため、コードは3を引きます。 (echo(!%~1!^^^)を使用して防ぐことができます。


可能な限り絶対的な最速のパフォーマンスが必要な場合は、- jebの答え引数付きの「バッチ」マクロ として使用できます。これは、DosTipsで開発された高度なバッチ技術であり、:subroutineを呼び出すという本質的に遅いプロセスを排除します。 バッチマクロの背後にある概念の詳細な背景 を取得できますが、そのリンクはより原始的であまり望ましくない構文を使用します。

以下は、最適化された@strLenマクロです。マクロと:subroutineの使用法の違い、およびパフォーマンスの違いを示す例があります。

@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
22
dbenham

最初の数行は、単に: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ループ」バージョンではそれほど効率的ではありません。しかし、理解するのは簡単で、3GHzコンピューターはほんの数秒で数千回の反復をすり抜けることができます。

9
Cody Barnes

はい、もちろん、vbscript(またはPowerShell)を使用する簡単な方法があります。

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

これをstrlen.vbsとしてコマンドラインに保存します

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

Forループを使用して結果をキャプチャします(またはスクリプトタスクにvbscript全体を使用します)

バッチを使用して面倒な回避策を作成する必要がありますが、vbscriptは各Windowsディストリビューション(および後のPowerShell)で使用できるため、使用しない理由はありません。

5
ghostdog74

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

上記のほとんどのソリューションよりも1桁簡単なエスケープ文字を処理し、ループ、マジックナンバー、DelayedExpansion、一時ファイルなどを含みません。

バッチスクリプト以外で使用する(手動でコンソールにコマンドを入力することを意味する)場合は、%%RESULT%%キーを%RESULT%に置き換えます。

必要に応じて、%ERRORLEVEL%変数を任意のNOPコマンドを使用してFALSEに設定できます。 echo. >nul

5
Alexander

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を使用していますが、これは私のために働いています。

3
Farrukh Waheed

Jmh_grの 2行アプローチ が気に入っています。

リダイレクトの前にコマンドの部分を()で囲む場合を除き、1桁の数字では機能しません。 1>は特別なコマンドであるため、「Echo is On」がファイルにリダイレクトされます。

この例では、1桁の数字を処理しますが、文字列に含まれる<などの他の特殊文字は処理しません。

(ECHO %strvar%)> tempfile.txt
2
OnlineOverHere
@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 =を設定するように変更します。これを将来の参照用にここに配置します。

1
unpredictubl

文字列の長さを数行で計算する、もう1つのバッチスクリプト。最速ではないかもしれませんが、かなり小さいです。サブルーチン「:len」は、2番目のパラメーターで長さを返します。最初のパラメーターは、分析される実際の文字列です。注-特殊文字はエスケープする必要があります。これは、バッチファイル内の文字列の場合です。

@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
1
Silkworm

Much 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は、推定される最大文字列長です。必要に応じて変更してください。

0
user10744194
@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 ########################
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
0
John