1

Тема: CMD/BAT: Нахождение длины строки

Именно такой темы в коллекции нет, хотя в некоторых других темах этот вопрос косвенно подымался. Думаю, что решение данной задачи должно содержаться в коллекции.

Существуют разные способы, как найти длину строки. Рассмотрим их и проведем анализ производительности. В конечном счете выбор должен пасть на самый быстрый вариант решения.

Для замера производительности я использовал следующий шаблон:

@ echo off
setlocal enabledelayedexpansion

call :getTick
set start=%errorlevel%

for /l %%i in (1,1,100) do тут_вызов_функции

call :getTick
set /a long=%errorlevel%-start
echo %long%

pause
exit /b 0

:getTick
:: Количество сотых долей секунды, прошедших с начала дня
  setlocal
  set t=%time: =0%
  set /a tick=1%t:~9,2%-100+(1%t:~6,2%-100)*100+(1%t:~3,2%-100)*6000+(1%t:~0,2%-100)*360000
endlocal & exit /b %tick%

Т.е. будем запускать функцию определения длины строки 100 раз и замерять время работы. Определять будем длину переменной %Path%, в моем случае она составляет 388 символов - достаточно длинная строка.

Первый вариант решения задачи (решение в лоб):

:length
:: Нахождение длины строки
:: %1 - строка
:: Результат помещается в %errorlevel%
  setlocal
  set len=0
  if "%~1"=="" goto end
  set str=%~1
  :loop
  set /a len+=1
  if not "!str:~%len%!"=="" goto loop
  :end
endlocal & exit /b %len%

Отработал за 1602, т.е. 16 секунд.

Второй вариант (записываем строку в файл, после чего определяем размер файла):

:length
:: Нахождение длины строки
:: %1 - строка
:: Результат помещается в %errorlevel%
  setlocal
  :length1
  set "tempFolder=%TEMP%\%~n0.%time:~-2%.%random%"
  md "%tempFolder%" 2>nul || goto length1
  set "tmpFile=%tempFolder%\1"
  <nul set /p ".=%1">"%tmpFile%"
  for /f %%f in ("%tmpFile%") do set /a "len=%%~zf"
  rd /s /q "%tempFolder%"
endlocal & exit /b %len%

Этот вариант отработал за 33, т.е. существенно меньше секунды. Идея создание временного файла взята из обсуждения в этой теме: http://forum.script-coding.com/viewtopic.php?id=7944

Чем же так плох первый вариант? Возможно это связано с циклом goto. В любом случае остановиться надо на втором варианте.

2 (изменено: wisgest, 2013-01-06 21:15:36)

Re: CMD/BAT: Нахождение длины строки

Arigato пишет:

Именно такой темы в коллекции нет, хотя в некоторых других темах этот вопрос косвенно подымался. Думаю, что решение данной задачи должно содержаться в коллекции.

Да, именно такой темы — нет. Вопрос поднимался, причём, отнюдь не косвенно. Да — должно содержаться. Причём, наряду с другими возможными решениями, на мой взгляд, должно присутствовать и «решение в лоб». Хотя не уверен, что следовало бы создавать отдельную тему, может быть, лучше было бы собрать в одну тему различные функции обработки строк, например, кроме нахождения длины: поиск вхождения подстроки, может быть, Trim

По поводу второго варианта. Создание каталога, там где можно ограничится лишь файлом, представляется избыточным. Можно воспользоваться тем, что пока файл открыт на запись, любые посторонние попытки его изменения вызовут ошибку:

  :length1
  set "tmpFile=%TEMP%\%~n0.%time:~-2%.%random%"
  (
    (
      <nul set /p ".=%1"
      for /f %%f in ("%tmpFile%") do set /a "len=%%~zf"
    ) >"%tmpFile%" || goto length1
    del %tmpFile% >nul
  ) 2>nul

(и этот подход мне представляется более надёжным.)

Замеров времени я не проводил, но чисто умозрительно — должно быть чуть-чуть быстрее.

3

Re: CMD/BAT: Нахождение длины строки

Будет ли файл занят на протяжении всей скобки?

    (
      <nul set /p ".=%1"
      for /f %%f in ("%tmpFile%") do set /a "len=%%~zf"
    ) >"%tmpFile%" || goto length1

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

4 (изменено: wisgest, 2013-01-07 01:22:40)

Re: CMD/BAT: Нахождение длины строки

Arigato пишет:

Будет ли файл занят на протяжении всей скобки?

Да, в том всё и дело, чтобы заблокировать файл, пока не будет прочитан его размер.

Впрочем, можно проверить

(echo 123
pause>con)>tmp.txt

— не выйдя из паузы, не получится изменить/удалить tmp.txt

Схожий приём используется, например, в теме CMD/BAT: запуск единственной копии сценария.

5

Re: CMD/BAT: Нахождение длины строки

Мне гораздо больше нравится другой вариант скрипта:


:strLength string len
(   SETLOCAL ENABLEDELAYEDEXPANSION
    set "str=A!%~1!"
    set "len=0"
    for /L %%A in (12,-1,0) do (
        set /a "len|=1<<%%A"
        for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"
    )
)
( ENDLOCAL & REM RETURN VALUES
    IF "%~2" NEQ "" SET /a %~2=%len%
)
EXIT /b

З.Ы. По шаблону скорости - опережает все варианты.

6

Re: CMD/BAT: Нахождение длины строки

urahangka, приведите пример использования. У меня не выходит.

7

Re: CMD/BAT: Нахождение длины строки

скорость не знаю


echo off
call :len "dfljgkdflgljkgsdfg"
:return
call echo %len%
exit

:len (str) {
    set out=%~0
    set out=%out:~1%
    set "str=%~1"
    setlocal ENABLEDELAYEDEXPANSION
    for /l %%x in (0,1,99999999999999999999) do (
        call set t=!t!%%str:~%%~x,1%%
        if !t!==!str! set %out%=%%x & call :return
    )
    exit /b
}
Я конечно далек от мысли... (с)

8

Re: CMD/BAT: Нахождение длины строки

smaharbA пишет:

echo off
call :len "dfljgkdflgljkgsdfg"
:return
call echo %len%
exit

:len (str) {
...
call :return
...
    exit /b
}

очень странный способ выхода из подпрограммы. А если понадобятся два вызова? Как выполнить выход в таком случае?


call :len "one"
call echo %len%
call :len "two"
call echo %len%

Правильный код: вместо call :return написать goto :EOF или exit /b

( 2 * b ) || ! ( 2 * b )

9

Re: CMD/BAT: Нахождение длины строки

попробуйте

Я конечно далек от мысли... (с)

10

Re: CMD/BAT: Нахождение длины строки

Хорошо. Проверил.

Вот слегка модифицированный код. В чем его смысл?


echo off
call :len "one"
:return
call echo %len%
exit /b

:len (str) {
    set out=%~0
    set out=%out:~1%
    set "str=%~1"
    setlocal ENABLEDELAYEDEXPANSION
    for /l %%x in (0,1,9) do (
        call set t=!t!%%str:~%%~x,1%%
        if !t!==!str! set %out%=%%x & call :return
    )
    exit /b
}

Вот результат его работы:


U:\tmp>echo off
2
3
4
5
6
7
8
9
ECHO is off.
( 2 * b ) || ! ( 2 * b )

11

Re: CMD/BAT: Нахождение длины строки

Вам еще учиться (без балды)

Я конечно далек от мысли... (с)

12 (изменено: Rumata, 2013-04-02 00:55:28)

Re: CMD/BAT: Нахождение длины строки

smaharbA пишет:

Вам еще учиться (без балды)

Очень интересное высказывание на критику неправильного кода. Никто не совершенен. А дополнительные знания никому еще были лишними.

( 2 * b ) || ! ( 2 * b )

13 (изменено: urahangka, 2013-04-02 11:02:19)

Re: CMD/BAT: Нахождение длины строки

alexii пишет:

urahangka, приведите пример использования. У меня не выходит.


@echo off
set str=super-puper-stroka, izmeryaem dliny.
call :strLen str dlina
echo stroka=%str%
echo length=%dlina%
pause
exit /b

:strLen stroka length
(   SETLOCAL ENABLEDELAYEDEXPANSION
    set "str=A!%~1!"
    set "len=0"
    for /L %%A in (12,-1,0) do (
        set /a "len|=1<<%%A"
        for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"
    )
)
( ENDLOCAL & REM RETURN VALUES
    IF "%~2" NEQ "" SET /a %~2=%len%
)
EXIT /b

14 (изменено: urahangka, 2013-04-02 12:08:50)

Re: CMD/BAT: Нахождение длины строки

Кстати, нарыл где-то в недрах интернета функцию скрипта бинарного подсчета длины строки:


@echo off
set str=super-puper-stroka, izmeryaem dliny.
call :strlen4 str dlina
echo stroka=%str%
echo length=%dlina%
pause
exit /b

:strlen4  StrVar  RtnVar  --  be sure to check if the returned errorlevel is 0
(   setlocal enabledelayedexpansion & set /a "}=0"
    if "%~1" neq "" if defined %~1 (
        for %%# in (4096 2048 1024 512 256 128 64 32 16) do (
            if "!%~1:~%%#,1!" neq "" set "%~1=!%~1:~%%#!" & set /a "}+=%%#"
        )
        set "%~1=!%~1!0FEDCBA9876543211" & set /a "}+=0x!%~1:~32,1!!%~1:~16,1!"
    )
)
endlocal & set /a "%~2=%}%" & exit /b

З.Ы. Очень шустрый! Быстрее выложенного мной ранее где-то в 3 раза.

Лениво делать скриншоты, так что просто выложу результаты тестирования:
Исходные данные:
"деревянный" комп на winXP SP3 (Core 2 Duo  2Гц, 2 Гб оперы)
строка - длина 54 символа
цикл - 1000 раз
тики (сотые доли секунды) - перед циклом и после цикла. Результат - разница между тиками.

Вариант через временный файл:     9652 тика
Вариант, выложенный мной ранее: 1156 тика
Этот Вариант:                                   355 тиков.

15

Re: CMD/BAT: Нахождение длины строки

urahangka, ограничение в 4К вызвано чем - ограничением на длину строки в командной строке?

( 2 * b ) || ! ( 2 * b )

16 (изменено: urahangka, 2013-04-02 12:50:58)

Re: CMD/BAT: Нахождение длины строки

Rumata пишет:

urahangka, ограничение в 4К вызвано чем - ограничением на длину строки в командной строке?

Если память не отшибло, то стандартное ограничение строки - 1023. Насколько я понимаю данный скрипт по тестам, он подсчитывает длину строки вплоть до 8189 символов, если строка длиннее - все равно показывает длину 8189 символов.

17

Re: CMD/BAT: Нахождение длины строки

за скорость не знаю


@echo off & call :%~1 "%~2" 2> nul

set me=%~0
echo %time%
for /l %%x in (1,1,100) do call :len "sdfsdf"
echo %ERRORLEVEL%
echo %time%
exit

:len (str) {
    if "%~1"=="" exit /b 0 else cmd /c ""%me%" length "%~1""
    exit /b %ERRORLEVEL%
}

:length (str) {
    set "str=%~1"
     setlocal ENABLEDELAYEDEXPANSION
    for /l %%x in (0,1,99999999999999999999) do (
        call set x=!x!%%str:~%%~x,1%%
        if "!x!"=="!str!" set /a x=%%~x+1 & exit !x!
    )
    exit 0
}
Я конечно далек от мысли... (с)

18

Re: CMD/BAT: Нахождение длины строки

Скорость хорошая, практически идентичная скрипту, выложенному мной ранее(тест выдает порядка 370 тиков). Но мне не нравится двойная процедура, юзабилити не очень. Хотя, конечно, можно переделать...

19 (изменено: XGuest, 2013-04-07 23:56:19)

Re: CMD/BAT: Нахождение длины строки

Доброго времени суток

Быстрей не видел

@echo off
 call :len var "строка"                                 &:: "var" - Переменная куда вернуть значение
 echo Длина строки: %var%                      
goto :eof

:len
 set len=%~2
 if not "%len%"=="" set /a %1+=1 & call :len %1 "%len:~1%"
goto :eof

@echo off
set VAR=Очень длинная строка
set BAK=%TMP%\%RANDOM%
>%BAK% echo.%VAR%
for %%I in (%BAK%) do set /a len=%%~zI-2
echo Длина строки: %len%
goto :eof

С наилучшими пожеланиями
XGuest

20

Re: CMD/BAT: Нахождение длины строки

XGuest пишет:

Доброго времени суток

Быстрей не видел

К сожалению, тест показывает, что это самые долгие варианты функции. (1-й вариант более 24000 тиков, второй порядка 10000 тиков.)

Быстрее функции бинарного поиска, выложенного мной ранее, вариантов нету.

21

Re: CMD/BAT: Нахождение длины строки

а так можно ?

call :len "строка"                                 &:: "len" - Переменная куда вернуть значение
 echo Длина строки: %len%
Я конечно далек от мысли... (с)