Я рекомендую сначала прочитать Почему после использования команды 'set var = text' в командной строке не выводится строка с 'echo% var%'? и посмотрите на строку set b = !initial!
, которая определяет переменную средыс именем b
(регистронезависимая интерпретируемая маленькая буква B и пробел) с пробелом и строкой, считанной из текстового файла в качестве значения.
Командный процессор Windows не поддерживает переход к метке внутри тела FOR .
Метки внутри тела FOR приводят к неопределенному поведению выполнения скрипта.
Возможно использовать подпрограмму, хотя это значительно увеличивает командный файлвремя исполнения.См. Почему цикл GOTO намного медленнее цикла FOR и дополнительно зависит от источника питания? , чтобы узнать, как была решена одна задача с четырьмя различными решениями и сколько времени требуется для выполнения задачи с каждым решением.
Вот мое решение для этой задачи: вызов подпрограммы для каждой строки для подсчета запятых и точек в текущей строке:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "TotalCountComma=0"
set "TotalCountDot=0"
for /F "usebackq delims= eol=" %%I in ("laba2.txt") do (
set "Line=%%I"
call :Count
)
if %TotalCountComma% == 1 (set "CommaS=") else set "CommaS=s"
if %TotalCountDot% == 1 (set "DotS=") else set "DotS=s"
echo The file contains %TotalCountComma% comma%CommaS% and %TotalCountDot% dot%DotS%.
endlocal
goto :EOF
:Count
setlocal EnableDelayedExpansion
set "LineCountComma=0"
set "LineCountDot=0"
set "LineLength=1"
for /L %%J in (0,1,8192) do (
if "!Line:~%%J,1!" == "" goto ExitLoop
if "!Line:~%%J,1!" == "," set /A LineCountComma+=1
if "!Line:~%%J,1!" == "." set /A LineCountDot+=1
)
:ExitLoop
endlocal & set /A "TotalCountComma+=%LineCountComma%" & set /A "TotalCountDot+=%LineCountDot%"
goto :EOF
FOR с параметром /F
читает текстfile laba2.txt
построчно и игнорирует пустые строки.
По умолчанию игнорируются FOR , также все строки, начинающиеся с точки с запятой, поскольку eol=;
является значением по умолчанию для конца строкивариант.Это поведение изменяется с помощью eol=
, который указывает отсутствие символа конца строки.
FOR по умолчанию разбивает каждую строку на подстроки, используя обычный пробел и горизонтальную табуляцию в качестве разделителей строк, и назначает только первыйСтрока с разделителями / табуляцией в указанной переменной цикла.Такое поведение разделения строк здесь также нежелательно.delims=
определяет пустой список разделителей, который отключает разделение строк.
Опция usebackq
используется, поскольку строка в двойных кавычках должна интерпретироваться как имя файла, строки которого должны читаться, а не какстрока для обработки.В этом особом случае двойные кавычки вокруг имени файла laba2.txt
не понадобятся, что также сделает бесполезным использование usebackq
.
delims=
и eol=
могут быть указаныв строке параметров в двойных кавычках, но только в этом порядке."usebackq eol= delims="
не будет работать, потому что это определяет пробел как символ конца строки и пустой список разделителей.
Так что FOR с используемыми параметрами назначает каждую непустую строку, считанную из указанногофайл в указанную переменную цикла I
и запускает команды внутри тела цикла FOR .
Строка назначается рядом с переменной среды с именем Line
.Это делается, когда отложенное расширение переменной среды отключено, что важно для текущей строки, содержащей один или несколько восклицательных знаков.В противном случае командный процессор Windows будет анализировать с включенным отложенным расширением командной строки set "Line=%%I"
во второй раз после замены %%I
текущим значением переменной цикла I
и будет интерпретировать !
как начало / конецпеременная окружения, значение которой ссылается на задержку.Такое поведение здесь определенно нежелательно, так как оно приведет к изменению строки при назначении ее переменной среды Line
, в большинстве случаев удаляя части строки.
Но задержанное расширение необходимо для подсчета запятых иточки в линии.Можно было бы включить отложенное расширение после командной строки set "Line=%%I"
с помощью setlocal EnableDelayedExpansion
и использовать endlocal
в качестве последней командной строки в теле цикла FOR .Но это невозможно здесь.По этой причине прочитайте этот ответ с подробной информацией о командах SETLOCAL и ENDLOCAL .
Поэтому для обработки текущей строки вызывается подпрограмма с call :Count
.Подпрограмма использует цикл FOR с параметром /L
для сравнения каждого символа текущей строки с ,
и .
, который завершается при достижении конца строки с помощью перехода к метке ниже ЗА петля.Этот цикл FOR значительно ускоряет обработку строки по сравнению с чистым циклом GOTO , который также возможен, но намного медленнее.
Командная строка после :ExitLoop
опять что-то особенное.Команда endlocal
восстановит предыдущее окружение, что означает, что переменные окружения LineCountComma
и LineCountDot
больше не существуют, по крайней мере, со значениями, установленными выше и внутри цикла FOR подпрограммы.Таким образом, в одной командной строке указаны три команды, которые cmd.exe
выполняется одна за другой, потому что оператор &
заменяет %LineCountComma%
и %LineCountDot%
текущими значениями этих двух переменных среды перед выполнением этой командной строки стри команды.Таким образом, endlocal
удаляет переменные окружения LineCountComma
и LineCountDot
, но их значения уже представлены в виде строк в командной строке, поэтому значения проходят через локальный барьер окружения.
После чтения каждой строки из текстового файлаи обрабатывая каждую непустую строку в подпрограмме Count
, значения двух общих счетчиков выводятся перед восстановлением исходной среды и выходом из выполнения пакетного файла с помощью goto :EOF
(если расширения команд были включены до запуска этого пакетного файла, как в Windowsпо умолчанию).
Я рекомендую прочитать Почему в Windows вообще есть ограничение на переменные среды? относительно значения 8192
для максимальной длины строки.В приведенном выше коде используется set "Line=%%I"
.Максимальная длина строки, считываемой из текстового файла, в этом случае может составлять 8183
символов из-за ограничения командного процессора 8191
байтов минус 4
байтов для имени переменной Line
минус 1
байт для =
минус 2
байт для двух двойных кавычек и наиболее вероятно минус 1
байт для завершения нольбайт.Таким образом, в действительности 8182
(индекс символа 8183) можно использовать вместо 8192
в цикле как абсолютный максимум.
Чтобы понять используемые команды и то, как они работают, откройте окно командной строки,выполните там следующие команды и полностью прочитайте все страницы справки, отображаемые для каждой команды.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
set /?
setlocal /?
См. Также:
PS: Использование любого другого языка / интерпретатора сценариев, кроме командного процессора Windowscmd.exe
, предназначенный для выполнения команд и приложений, будет определенно лучше для подсчета запятых и точек в текстовом файле.е.