web-dev-qa-db-fra.com

Comment obtenez-vous la longueur de la chaîne dans un fichier batch?

Il ne semble pas y avoir de moyen facile d’obtenir la longueur d’une chaîne dans un fichier batch. Par exemple.,

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

Comment trouver la longueur de chaîne de MY_STRING?

Points bonus si la fonction de longueur de chaîne gère tous les caractères possibles des chaînes, y compris les caractères d'échappement, comme ceci: !%^^()^!.

59
indiv

Comme il n’existe pas de fonction intégrée pour la longueur de chaîne, vous pouvez écrire votre propre fonction comme suit: 

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

Cette fonction nécessite toujours 13 boucles, au lieu d'une simple fonction strlen qui nécessite des boucles de strlen.
Il gère tous les caractères.

79
jeb

Vous pouvez le faire sur deux lignes, entièrement dans un fichier de commandes, en écrivant la chaîne dans un fichier, puis en obtenant la longueur du fichier. Il vous suffit de soustraire deux octets pour prendre en compte le CR + LF automatique ajouté à la fin.

Supposons que votre chaîne se trouve dans une variable appelée strvar:

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

La longueur de la chaîne est maintenant dans une variable appelée strlength.

En détail un peu plus:

  • FOR %%? IN (filename) DO ( ...: obtient des informations sur un fichier
  • SET /A [variable]=[expression]: évalue l'expression numériquement
  • %%~z?: expression spéciale pour obtenir la longueur du fichier 

Pour écraser toute la commande en une seule ligne:

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

Je préfère la réponse acceptée par jeb } _ - c'est la solution la plus rapide connue et celle que j'utilise dans mes propres scripts. (En fait, quelques optimisations supplémentaires sont proposées sur DosTips, mais je ne pense pas qu'elles en valent la peine)

Mais il est amusant de proposer de nouveaux algorithmes efficaces. Voici un nouvel algorithme qui utilise l'option 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

Le code soustrait 3 parce que l'analyseur jongle avec la commande et ajoute un espace avant que CMD/V/C ne l'exécute. Il peut être évité en utilisant (echo(!%~1!^^^).


Pour ceux qui veulent la performance la plus rapide possible, réponse de jeb peut être adopté pour être utilisé en tant que "macro" batch avec arguments . Il s'agit d'une technique de traitement par lots avancée, développée sur DosTips, qui élimine le processus intrinsèquement lent de CALLing a: subroutine. Vous pouvez obtenir plus de détails sur les concepts des macros de lot ici }, mais ce lien utilise une syntaxe plus primitive et moins souhaitable.

Vous trouverez ci-dessous une macro @strLen optimisée, avec des exemples illustrant les différences entre l'utilisation de la macro et du: sous-programme, ainsi que les performances.

@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

- Exemple de sortie -

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

Les premières lignes servent simplement à démontrer la fonction: 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

Bien sûr, cela n’est pas aussi efficace avec la version à "13 boucles" fournie par jeb. Mais il est plus facile à comprendre et votre ordinateur 3GHz peut passer à travers quelques milliers d’itérations en une petite fraction de seconde.

8
Cody Barnes

Oui, bien sûr, il existe un moyen simple d'utiliser vbscript (ou Powershell). 

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

enregistrer ceci en tant que strlen.vbs et en ligne de commande

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

Utilisez une boucle for pour capturer le résultat (ou utilisez entièrement vbscript pour votre tâche de script)

Il est certes préférable d'éviter de créer des solutions de contournement lourdes à l'aide de batch et il n'y a aucune excuse pour ne pas l'utiliser car vbscript est disponible avec chaque distribution Windows (et PowerShell ultérieurement).

5
ghostdog74

Je viens de trouver la solution 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%

La sortie est:

string "abcdef!%^^()^!" length = 14

Il gère les caractères d'échappement, un ordre de grandeur plus simple que la plupart des solutions ci-dessus, et ne contient pas de boucles, de nombres magiques, d'expansion différée, de fichiers temporaires, etc.

En cas d'utilisation en dehors du script de traitement par lots (signifie que vous devez placer manuellement les commandes dans la console), remplacez la clé %%RESULT%% par %RESULT%.

Si nécessaire, la variable %ERRORLEVEL% peut être définie sur FALSE à l’aide d’une commande NOP, par exemple. echo. >nul

3
Alexander

Si vous utilisez Windows Vista +, essayez cette méthode Powershell:

For /F %%L in ('Powershell $Env:MY_STRING.Length') do (
    Set MY_STRING_LEN=%%L
)

ou bien:

Powershell $Env:MY_STRING.Length > %Temp%\TmpFile.txt
Set /p MY_STRING_LEN = < %Temp%\TmpFile.txt
Del %Temp%\TmpFile.txt

Je suis sur Windows 7 x64 et cela fonctionne pour moi.

3
Farrukh Waheed

J'aime l'approche deux lignes de jmh_gr.

Cela ne fonctionnera pas avec des nombres à un chiffre sauf si vous mettez () autour de la partie de la commande avant la redirection. étant donné que 1> est une commande spéciale "Echo is On" sera redirigé vers le fichier.

Cet exemple doit prendre en charge les nombres à un chiffre mais pas les autres caractères spéciaux tels que < qui peuvent être dans la chaîne.

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

définissez la variable sur la longueur désirée ou modifiez-la de manière à définir/p var = afin que l'utilisateur la saisisse . Mettez-la ici pour référence ultérieure.

1
unpredictubl

Juste un autre script de lot pour calculer la longueur d'une chaîne, en quelques lignes seulement. Ce n'est peut-être pas le plus rapide, mais c'est assez petit. Le sous-programme ": len" renvoie la longueur dans le deuxième paramètre. Le premier paramètre est la chaîne en cours d'analyse ..__ Veuillez noter que les caractères spéciaux doivent être ignorés, c'est le cas de toute chaîne du fichier de commandes.

@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

C'est beaucoup plus simple !

Solution de lot pure. Aucun fichier temporaire. Pas de longs scripts.

@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 est la longueur maximale estimée de la chaîne. Changez-le en fonction de vos besoins.

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

Je veux commencer par dire que je ne connais pas grand chose en écriture de code/script/etc. mais je pensais partager une solution que je semble avoir trouvée. La plupart des réponses ici me sont allées un peu au dessus de la tête, alors j’étais curieux de savoir si ce que j’ai écrit est comparable.

@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