windows - Hvordan får du strenglængden i en batchfil?

Indlæg af Hanne Mølgaard Plasc

Problem



Der ser ikke ud til at være en nem måde at få længden af ​​en streng i en batchfil på. For eksempel,


SET MY\_STRING=abcdefg
SET /A MY\_STRING\_LEN=???


Hvordan finder jeg strengens længde på MY\_STRING?


Bonuspunkter, hvis strenglængdefunktionen håndterer alle mulige tegn i strenge, herunder escape-tegn, som denne: !\%^^()^!.

Bedste reference


Da der ikke er indbygget funktion til strenglængde, kan du skrive din egen funktion som sådan:


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


Denne funktion har altid brug for 13 sløjfer, i stedet for en enkel strlen-funktion, der har brug for strlen-sløjfer.

Det håndterer alle tegn.

Andre referencer 1


Du kan gøre det i to linjer, helt i en batchfil, ved at skrive strengen til en fil og derefter få længden af ​​filen. Du skal bare trække to byte for at tage højde for den automatiske CR + LF, der er tilføjet til slutningen.


Lad os sige, at din streng er i en variabel kaldet strvar:


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


Strengen er nu i en variabel kaldet strlength.


I lidt mere detaljer:



  • FOR \%\%? IN (filename) DO ( ...: får information om en fil

  • SET /A [variable]=[expression]: Vurder udtrykket numerisk

  • \%\%~z?: Specielt udtryk for at få længden af ​​filen



At mash hele kommandoen i en linje:


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

Andre referencer 2


Jeg foretrækker jebs godkendte svar - det er den hurtigst kendte løsning og den, jeg bruger i mine egne scripts. (Faktisk er der nogle få ekstra optimeringer bandied om på DosTips, men jeg tror ikke, de er det værd) 37]]


Men det er sjovt at komme med nye effektive algoritmer. Her er en ny algoritme, der bruger indstillingen 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


Koden trækker 3, fordi parseren juggler kommandoen og tilføjer et mellemrum, før CMD/V/C udfører det. Det kan forhindres ved at bruge (echo(!\%~1!^^^).





For dem, der ønsker den absolutte hurtigste ydeevne, kan jebs svar vedtages til brug som en batch 'makro' med argumenter. Dette er en avanceret batchteknik devloped over på DosTips, der eliminerer den iboende langsom proces af CALLing a: subroutine. Du kan få mere baggrund på begreberne bag batchmakroer her, men det link bruger en mere primitiv, mindre ønskelig syntaks. [39] [40]


Nedenfor er en optimeret @strLen-makro med eksempler, der viser forskelle mellem makro og: subrutine brug, samt forskelle i ydeevne.


@echo off
setlocal disableDelayedExpansion

:: -------- Begin macro definitions ----------
set ^"LF=^
\%= This creates a variable containing a single linefeed (0x0A) character =\%
^"
:: Define \%
\% to effectively issue a newline with line continuation
set ^"
=^^^\%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 (\%
\%
  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=,

:: -------- 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


- Prøveudgang -


@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

Andre referencer 3


De første par linjer er simpelthen at demonstrere: strLen funktionen.


@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


Selvfølgelig er dette ikke helt så effektivt i '13 loop' -versionen fra jeb. Men det er lettere at forstå, og din 3GHz-computer kan glide gennem et par tusinde iterationer i en lille smule af et sekund.

Andre referencer 4


Ja, det er en nem måde at bruge vbscript (eller powerhell).


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


gem dette som strlen.vbs og på kommandolinjen


c:	est> cscript //nologo strlen.vbs "abcd"


Brug en for-loop til at fange resultatet (eller brug vbscript helt til din scripting opgave)


Bestemt slår at skulle skabe besværlige løsninger ved hjælp af batch og der er ingen undskyldning for ikke at bruge den, da vbscript er tilgængelig med hver Windows distribution (og powershell senere).

Andre referencer 5


Hvis du er i Windows Vista +, så prøv denne Powershell-metode:


For /F \%\%L in ('Powershell $Env:MY\_STRING.Length') do (
    Set MY\_STRING\_LEN=\%\%L
)


eller alternativt:


Powershell $Env:MY\_STRING.Length > \%Temp\%TmpFile.txt
Set /p MY\_STRING\_LEN = < \%Temp\%TmpFile.txt
Del \%Temp\%TmpFile.txt


Jeg er på Windows 7 x64 og dette virker for mig.

Andre referencer 6


Jeg kan godt lide den to line tilgang af jmh\_gr.


Det vil ikke virke med enkeltcifrede tal, medmindre du sætter () rundt om delen af ​​kommandoen før omdirigering. Siden 1> er en speciel kommando 'Echo er On' vil blive omdirigeret til filen.


Dette eksempel skal tage sig af enhedsnumre, men ikke de andre specialtegn som <, der kan være i strengen.


(ECHO \%strvar\%)> tempfile.txt

Andre referencer 7


Bare fundet ULTIMATE løsning:


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


Udgangen er:


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


Det håndterer undslippe tegn, en størrelsesorden enklere end de fleste løsninger ovenfor og indeholder ingen løkker, magiske tal, Forsinket udvidelse, temp filer osv.


Hvis brug udenfor batch script (betyder at sætte kommandoer til konsol manuelt), skal du erstatte \%\%RESULT\%\% -tasten med \%RESULT\%.


Hvis det er nødvendigt, kan \%ERRORLEVEL\% variabel indstilles til FALSE ved hjælp af en hvilken som helst NOP-kommando, f.eks. echo. >nul

Andre referencer 8


Bare et andet batch script til at beregne længden af ​​en streng, på bare nogle få linjer. Det kan ikke være det hurtigste, men det er ret lille. Underrutinen 'len' returnerer længden i den anden parameter. Den første parameter er den aktuelle streng, der analyseres.
Bemærk venligst - specialtegn skal undslippes, det er tilfældet med en hvilken som helst streng i batchfilen.


@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

Andre referencer 9


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


sæt varen til hvad du vil finde længden af ​​den eller skift den til sæt/p var=så brugeren indtaster den.
Sætter dette her til fremtidig reference.

Andre referencer 10


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

Andre referencer 11


Jeg vil gerne forord dette ved at sige, at jeg ikke ved meget om at skrive kode/script/osv., Men troede, jeg kunne dele en løsning, som jeg tilsyneladende har oplevet. De fleste af svarene her gik ganske lidt over mit hoved, så jeg var nysgerrig efter at vide, om hvad jeg skrev er sammenlignelig.


@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