繁体   English   中英

批处理文件FOR循环不会循环

[英]Batch file FOR loop won't loop

我正在做作业。 试图获取我的批处理文件以读取输入文件的各行,并弄清楚它有多少点和逗号。 但是,不管我做什么,循环只会运行一次,因此即使有多行,也只能正确处理文件的第一行。 我想念什么?

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 

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_commas%相应的变量( %count_dots%%count_commas% )中
  • echo显变量
  • 删除临时文件。

我建议先阅读在命令行上使用“ 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

带选项/F FOR laba2.txt读取文本文件laba2.txt ,并忽略空行。

缺省情况下,由于eol=;FOR也会忽略所有以分号开头的行eol=; 是行尾选项的默认选项。 使用eol=可以更改此行为, eol=不指定行尾字符。

默认情况下, FOR使用正常空格和水平制表符作为字符串定界符将每一行拆分为子字符串,并仅将第一个空格/制表符分隔的字符串分配给指定的循环变量。 这里也不需要这种行拆分行为。 delims=定义一个空的定界符列表,该列表禁止行拆分行为。

使用usebackq选项是因为双引号中的字符串应被解释为应读取行的文件名,而不是要处理的字符串。 在这种特殊情况下,不需要在文件名laba2.txt周围加上双引号,这也将导致usebackq使用usebackq无用。

delims= eol=可以在双引号的选项字符串中指定,但只能以此顺序指定。 "usebackq eol= delims="将不起作用,因为这将空格定义为行尾字符和空的定界符列表。

因此,具有使用的选项的FOR将将从指定文件读取的每个非空行分配给指定的循环变量I并在FOR循环主体中运行命令。

该行被分配到名称为Line环境变量旁边。 这是在禁用延迟的环境变量扩展的情况下完成的,这对于包含一个或多个感叹号的当前行很重要。 否则,Windows命令处理器将使用循环变量I的当前值替换%%I后,第二次使用启用的延迟扩展来解析命令行set "Line=%%I" ,并解释为! 作为其值被延迟引用的环境变量的开始/结束。 绝对不希望出现这种情况,因为这将导致在将其分配给环境变量Line时修改该行,并且在大多数情况下会删除该行的某些部分。

但是必须延迟扩展才能计算一行中的逗号和点。 在使用setlocal EnableDelayedExpansion将命令行set "Line=%%I" ,并使用endlocal作为FOR循环主体中的最后一个命令行,可以启用延迟扩展。 但这在这里是不可能的。 由于这个原因,请阅读此答案以及有关命令SETLOCALENDLOCAL的详细信息。

因此,使用call :Count子例程来处理当前行。 子程序使用for循环使用选项/L ,以对抗当前行的每一个字符比较,. 并在到达行尾时退出,并跳转至FOR循环下方的标签。 与纯GOTO循环相比,此FOR循环可显着加快行处理速度,这也是可能的,但速度慢得多。

:ExitLoop之后的命令行:ExitLoop很特别。 endlocal命令将恢复先前的环境,这意味着环境变量LineCountCommaLineCountDot不再存在,至少在子例程的FOR循环内和内部没有设置的值。 因此,在一个命令行上指定了三个命令,由于操作符&原因, cmd.exe一个接一个地执行。在用三个命令执行此命令行之前, %LineCountComma%%LineCountDot%被这两个环境变量的当前值替换命令。 因此, endlocal删除了环境变量LineCountCommaLineCountDot ,但是它们的值在命令行中已经是字符串,因此这些值会通过局部环境屏障。

从文本文件读取每一行并处理子例程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:使用Windows命令处理器cmd.exe以外的任何其他脚本语言/解释器设计用于执行命令和应用程序,对于计算文本文件中的逗号和点的任务肯定会更好。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM