Jak uzyskać długość łańcucha w pliku wsadowym?

Nie wydaje się być łatwym sposobem na uzyskanie długości łańcucha w pliku wsadowym. Np.,

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

Jak znaleźć Długość Łańcucha MY_STRING?

Punkty bonusowe, jeśli funkcja długości łańcucha obsługuje wszystkie możliwe znaki w łańcuchach, w tym znaki escape, jak to: !%^^()^!.

Author: indiv, 2011-04-30

12 answers

Ponieważ nie ma wbudowanej funkcji dla długości łańcucha, możesz napisać własną funkcję w następujący sposób:

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

Ta funkcja potrzebuje zawsze 13 pętli, zamiast prostej funkcji strlen, która potrzebuje pętli strlen.
Obsługuje wszystkie znaki.

 73
Author: jeb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-03 02:16:04

Można to zrobić w dwóch wierszach, w pełni w pliku wsadowym, zapisując łańcuch znaków do pliku, a następnie pobierając jego długość. Wystarczy odjąć dwa bajty, aby uwzględnić automatyczne CR + LF dodane na końcu.

Załóżmy, że Twój łańcuch jest w zmiennej o nazwie strvar:

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

Długość łańcucha jest teraz w zmiennej o nazwie strlength.

Nieco bardziej szczegółowo:

  • FOR %%? IN (filename) DO ( ...: pobiera informacje o pliku
  • SET /A [variable]=[expression] : Oceń wyrażenie numerycznie
  • %%~z?: wyrażenie specjalne do uzyskania długości pliku

Aby zmiksować całe polecenie w jednej linii:

ECHO %strvar%>x&FOR %%? IN (x) DO SET /A strlength=%%~z? - 2&del x
 27
Author: Joshua Honig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-19 18:54:29

Preferuję akceptowaną odpowiedź Jeba - jest to najszybsze znane rozwiązanie i to, którego używam we własnych skryptach. (W rzeczywistości jest kilka dodatkowych optymalizacji na temat Dostipów, ale myślę, że nie są tego warte) {]} Ale fajnie jest wymyślać nowe wydajne algorytmy. Oto nowy algorytm, który używa opcji 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

Kod odejmuje 3, ponieważ parser żongluje komendą i dodaje spację, zanim CMD /V / C ją wykona. Może być zapobiegać za pomocą (echo(!%~1!^^^).


Dla tych, którzy chcą absolutnie najszybszej możliwej wydajności, odpowiedź Jeba może być przyjęta do użycia jako wsadowe "makro" z argumentami . Jest to zaawansowana technika wsadowa rozwijana przez at DosTips, która eliminuje z natury powolny proces wywoływania podprogramu:. Możesz uzyskać więcej tła na temat pojęć stojących za makrami wsadowymi tutaj , ale ten link używa bardziej prymitywnej, mniej pożądanej składni.

Poniżej znajduje się zoptymalizowane makro @ strLen, z przykładami pokazującymi różnice między użyciem makra i podprogramu:, a także różnice w wydajności.

@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

-- Przykładowe Wyjście --

@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
 18
Author: dbenham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:28

Kilka pierwszych linii służy po prostu do zademonstrowania funkcji: 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

Oczywiście nie jest to tak efektywne w wersji" 13 loop " dostarczonej przez jeb. Ale jest to łatwiejsze do zrozumienia, a Twój komputer 3GHz może prześlizgnąć się przez kilka tysięcy iteracji w niewielkim ułamku sekundy.

 6
Author: Cody Barnes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-27 20:59:01

Tak, oczywiście jest prosty sposób, używając vbscript (lub powershell).

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

Zapisz to jako strlen.vbs i w wierszu poleceń

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

Użyj pętli for, aby uchwycić wynik (lub użyj VBScript całkowicie do zadania skryptowego)

Z pewnością przebije konieczność tworzenia uciążliwych obejść przy użyciu wsadowego i nie ma wymówki, aby go nie używać, ponieważ vbscript jest dostępny z każdą dystrybucją Windows (i powershell w późniejszym czasie).

 5
Author: ghostdog74,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-30 12:09:28

Jeśli korzystasz z systemu Windows Vista +, spróbuj użyć metody Powershell:

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

Lub alternatywnie:

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

Jestem na Windows 7 x64 i to mi działa.

 3
Author: Farrukh Waheed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-05 09:15:36

Lubię podejście dwuliniowe jmh_gr.

Nie będzie działać z numerami jednocyfrowymi, chyba że umieścisz () wokół części polecenia przed przekierowaniem. ponieważ {[2] } jest specjalne polecenie "Echo jest włączone" zostanie przekierowane do pliku.

Ten przykład powinien uwzględniać liczby jednocyfrowe, ale nie inne znaki specjalne, takie jak <, które mogą znajdować się w łańcuchu.

(ECHO %strvar%)> tempfile.txt
 2
Author: OnlineOverHere,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:08

Właśnie znalazłem ostateczne rozwiązanie:

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%

Wyjście to:

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

Obsługuje znaki ucieczki, o rząd wielkości prostsze niż większość powyższych rozwiązań, i nie zawiera pętli, magicznych liczb, DelayedExpansion, plików tymczasowych itp.

W przypadku użycia poza skryptem wsadowym (czyli ręcznego umieszczania poleceń na konsoli), zastąp klawisz %%RESULT%% na %RESULT%.

W razie potrzeby zmienną %ERRORLEVEL% można ustawić na FALSE za pomocą dowolnego polecenia NOP, np. echo. >nul

 2
Author: Alexander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-12 16:27:55

Kolejny skrypt wsadowy do obliczania długości ciągu, w kilku wierszach. Może nie jest najszybszy, ale jest dość mały. Podprogram": len " zwraca długość w drugim parametrze. Pierwszym parametrem jest analizowany ciąg znaków. Uwaga-znaki specjalne muszą być unikalne, tak jest w przypadku dowolnego ciągu w pliku wsadowym.

@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
Author: Silkworm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-09 19:11:47
@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)

Ustaw zmienną na dowolną jej długość lub zmień ją na set / P var= tak, aby użytkownik ją wprowadził. Umieszczam to tutaj dla przyszłych odniesień.

 1
Author: unpredictubl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-11 18:03:20
@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
Author: eddie Duncan-Dunlop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-14 10:17:37

Chcę to poprzedzić mówiąc, że nie wiem zbyt wiele o pisaniu kodu/skryptu / itp. ale pomyślałem, że podzielę się rozwiązaniem, które wymyśliłem. Większość odpowiedzi tutaj trochę przeszło mi przez głowę, więc byłem ciekaw, czy to, co napisałem, jest porównywalne.

@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
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-10 11:26:37