Как вы получаете длину строки в пакетном файле? - PullRequest
61 голосов
/ 30 апреля 2011

Кажется, нет простого способа получить длину строки в командном файле. Например.,

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

Как найти длину строки MY_STRING?

Бонусные баллы, если функция длины строки обрабатывает все возможные символы в строках, включая escape-символы, например: !%^^()^!.

Ответы [ 14 ]

79 голосов
/ 30 апреля 2011

Поскольку встроенной функции для длины строки не существует, вы можете написать свою собственную функцию следующим образом:

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

Для этой функции всегда требуется 13 циклов вместо простой функции strlen, для которой нужны циклы strlen.
Он обрабатывает все символы.

29 голосов
/ 19 декабря 2011

Вы можете сделать это в две строки, полностью в пакетном файле, записав строку в файл и затем получив длину файла. Вам просто нужно вычесть два байта, чтобы учесть автоматический CR + LF, добавленный в конец.

Допустим, ваша строка находится в переменной с именем 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?: специальное выражение для получения длины файла

Чтобы объединить всю команду в одну строку:

ECHO %strvar%>x&FOR %%? IN (x) DO SET /A strlength=%%~z? - 2&del x
20 голосов
/ 18 декабря 2012

Я предпочитаю Принятый ответ Джеба - это самое быстрое из известных решений, которое я использую в своих сценариях.(На самом деле в 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

Код вычитает 3, потому что анализатор выполняет жонглирование команды и добавляет пробел перед тем, как CMD / V / C ее выполнит.Это можно предотвратить, используя (echo(!%~1!^^^).


Для тех, кто хочет максимально возможной производительности, ответ Джеба может быть принят для использования в качестве макроса batch ""с аргументами .Это продвинутая пакетная техника, разработанная в DosTips, которая устраняет медленный процесс вызова C: подпрограммы.Вы можете получить дополнительную информацию о концепциях пакетных макросов здесь , но эта ссылка использует более примитивный, менее желательный синтаксис.

Ниже приведен оптимизированный макрос @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

- Пример вывода -

@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
8 голосов
/ 28 июня 2013

Первые несколько строк просто демонстрируют функцию: 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

Конечно, это не так эффективно в версии "13 loop", предоставляемой jeb. Но это легче понять, и ваш компьютер 3GHz может пройти несколько тысяч итераций за небольшую долю секунды.

5 голосов
/ 30 апреля 2011

Да, конечно, есть простой способ, используя vbscript (или powershell).

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

сохранить это как strlen.vbs и в командной строке

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

Используйте цикл for для захвата результата (или полностью используйте vbscript для задачи скриптинга)

Конечно, это лучше, чем создавать громоздкие обходные пути, используя пакет, и нет никаких оснований не использовать его, так как vbscript доступен в каждом дистрибутиве Windows (и powershell позже).

4 голосов
/ 12 февраля 2017

Только что найдено 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

Он обрабатывает управляющие символы, на порядок проще, чем большинство решений выше, и не содержит циклов,магические числа, DelayedExpansion, временные файлы и т. д.

В случае использования вне пакетного сценария (имеется в виду ввод команд на консоль вручную), замените клавишу %%RESULT%% на %RESULT%.

При необходимости,Переменная %ERRORLEVEL% может быть установлена ​​на FALSE с помощью любой команды NOP, например, echo. >nul

3 голосов
/ 05 марта 2014

Если вы используете 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, и это работает для меня.

2 голосов
/ 19 сентября 2012

Мне нравится двухстрочный подход из jmh_gr.

Он не будет работать с однозначными числами, если вы не введете () вокруг части команды перед перенаправлением. Поскольку 1> - это специальная команда «Echo is On» будет перенаправлена ​​в файл.

Этот пример должен заботиться об однозначных числах, но не о других специальных символах, таких как <, которые могут быть в строке.

(ECHO %strvar%)> tempfile.txt
1 голос
/ 11 июля 2015
@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 любую длину, которую вы хотите найти, или измените ее на set / p var =, чтобы пользователь вводил ее. Поместите это здесь для дальнейшего использования.

1 голос
/ 09 апреля 2014

Просто еще один пакетный скрипт для вычисления длины строки всего за несколько строк.Возможно, он не самый быстрый, но довольно маленький.Подпрограмма ": len" возвращает длину во втором параметре.Первый параметр - это фактическая строка, которая анализируется.Обратите внимание - специальные символы должны быть экранированы, то есть в случае любой строки в командном файле.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...