Пакетный файл для цикла не будет цикл - PullRequest
0 голосов
/ 26 декабря 2018

Я делаю задание.Попытка заставить мой командный файл прочитать отдельные строки входного файла и выяснить, сколько у него точек и запятых.Однако, независимо от того, что я делаю, цикл запускается только один раз, поэтому правильно обрабатывается только первая строка файла, даже если есть несколько строк.Что мне не хватает?

REM @echo off
setlocal enabledelayedexpansion
set count=-2
for /F "delims=" %%I in (laba2.txt) DO (
set initial=%%I
set b = !initial!
:againdot
set oldb=!b!
set "b=!b:*.=!"
set /a count+=1
echo "b = !b!, oldb = !oldb!"
if not !oldb! == !b! goto :againdot
set b=!initial!
:againcomma
set oldb=!b!
set "b=!b:*,=!"
set /a count+=1
echo "b = !b!, oldb = !oldb!"
if not !oldb! == !b! goto :againcomma
)
echo %count%

Я, конечно, читал другие подобные вопросы, но я не могу понять, как, если вообще, они относятся к моей проблеме, так что извините, если этодубликат.

О, и если у вас есть лучший способ подсчета отдельных символов, я был бы рад услышать это, но это не главное.

РЕДАКТИРОВАТЬ: вот подача CMD

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>test.bat

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>REM @echo off

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>setlocal enabledelayedexpansion

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set count=-2

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>for /F "delims=" %I in (laba2.txt) DO (
set initial=%I
 set b = !initial!
 set oldb=!b!
 set "b=!b:*.=!"
 set /a count+=1
 echo "b = !b!, oldb = !oldb!"
 if not !oldb! == !b! goto :againdot
 set b=!initial!
 set oldb=!b!
 set "b=!b:*,=!"
 set /a count+=1
 echo "b = !b!, oldb = !oldb!"
 if not !oldb! == !b! goto :againcomma
)

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>(
set initial=this,is .a random
 set b = !initial!
 set oldb=!b!
 set "b=!b:*.=!"
 set /a count+=1
 echo "b = !b!, oldb = !oldb!"
 if not !oldb! == !b! goto :againdot
 set b=!initial!
 set oldb=!b!
 set "b=!b:*,=!"
 set /a count+=1
 echo "b = !b!, oldb = !oldb!"
 if not !oldb! == !b! goto :againcomma
)
"b = *.=, oldb = "

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set oldb=!b!

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set "b=!b:*.=!"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set /a count+=1

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>echo "b = !b!, oldb = !oldb!"
"b = =, oldb = *.="

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>if not !oldb! == !b! goto :againdot

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set oldb=!b!

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set "b=!b:*.=!"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set /a count+=1

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>echo "b = !b!, oldb = !oldb!"
"b = =, oldb = ="

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>if not !oldb! == !b! goto :againdot

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set b=!initial!

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set oldb=!b!

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set "b=!b:*,=!"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set /a count+=1

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>echo "b = !b!, oldb = !oldb!"
"b = is .a random, oldb = this,is .a random"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>if not !oldb! == !b! goto :againcomma

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set oldb=!b!

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set "b=!b:*,=!"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set /a count+=1

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>echo "b = !b!, oldb = !oldb!"
"b = is .a random, oldb = is .a random"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>if not !oldb! == !b! goto :againcomma

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>echo 3
3

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>

И вот файл, который я пытаюсь прочитать:

this,is .a random
..,arran,gement of.

commas  and,
 dots 

Ответы [ 2 ]

0 голосов
/ 26 декабря 2018

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

0 голосов
/ 26 декабря 2018

Командный процессор Windows не поддерживает переход к метке внутри тела FOR.Метки внутри тела FOR приводят к неопределенному поведению выполнения скрипта.

@ Mofi сказал в комментариях

Таким образом, вы не можете сделать это таким образом!Если вы поняли, вы хотите создать пакетный файл, который определит количество точек и запятых в файле.Я полностью изменил ваш файл и написал новый:

@echo off

set /a "count_dots=0"
set /a "count_commas=0"

powershell.exe -NoP -C "(Get-Content laba2.txt) -Replace '!',''|Set-Content $env:TEMP\laba2_tmp.txt"
setlocal EnableDelayedExpansion
for /f "delims= eol=" %%A IN (%TEMP%\laba2_tmp.txt) do (
    set line=%%A

    rem Find size of variable:
    (echo !line!)>%TEMP%\temp.txt
    for %%B IN (%TEMP%\temp.txt) do set size=%%~zB

    rem Check if the line has got commas:
    if not "!line:,=!" == "!line!" (
        rem Find size of variable *without* commas:
        (echo !line:,=!)>%TEMP%\temp.txt
        for %%B IN (%TEMP%\temp.txt) do set sizecommas=%%~zB

        rem Find difference of previous and new size of variable:
        if !size! NEQ !sizecommas! (set /a "diffcommas=size-sizecommas" & set /a "count_commas+=diffcommas")


    rem Check if the line has got dots:
    if not "!line:.=!" == "!line!" (
        rem Find size of variable *without* dots:
        (echo !line:.=!)>%TEMP%\temp.txt
        for %%B IN (%TEMP%\temp.txt) do set sizedots=%%~zB

        rem Find difference of previous and new size of variable:
        if !size! NEQ !sizedots! (set /a "diffdots=size-sizedots" & set /a "count_dots+=diffdots")
        )
    )
)

rem Delete temporary files:
del %TEMP%\temp.txt %TEMP%\laba2_tmp.txt

if %count_dots% == 1 (echo Found %count_dots% dot in specified document.) else (echo Found %count_dots% dots in specified document.)
if %count_commas% == 1 (echo Found %count_commas% comma in specified document.) else (echo Found %count_commas% commas in specified document.)

pause

Да, это правда, что он довольно большой, но я не мог упростить его дальше по причине:)!

Этот файл:

  • Устанавливает для требуемых переменных значение 0.
  • Имеет цикл for, который перебирает текстовый файл laba2.txt.
    • Только если строка (%%A) содержит точки (.) или запятые (,) будут обрабатываться.
    • Находит размер строки с помощью другого цикла for.
    • Находит размер строки без точек (.).
    • То же самое для запятых (,).
    • В обоих случаях мынайти разницу предыдущего размера строки и текущего размера строки.Суммируем результат в соответствующей переменной (%count_dots% или %count_commas%)
  • echo переменные
  • Удаление временных файлов.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...