这是 FOR /F 的设计行为 - 它从不返回空行。解决方法是使用 FIND 或 FINDSTR 在行前加上行号。如果您可以保证没有行以行号分隔符开头,那么您只需设置适当的分隔符并保留标记 1* 但只使用第二个标记。
::preserve blank lines using FIND, assume no line starts with ]
::long lines are truncated
for /f "tokens=1* delims=]" %%A in ('type "file.txt" ^| find /n /v ""') do echo %%B
::preserve blank lines using FINDSTR, assume no line starts with :
::long lines > 8191 bytes are lost
for /f "tokens=1* delims=:" %%A in ('type "file.txt" ^| findstr /n "^"') do echo %%B
::FINDSTR variant that preserves long lines
type "file.txt" > "file.txt.tmp"
for /f "tokens=1* delims=:" %%A in ('findstr /n "^" "file.txt.tmp"') do echo %%B
del "file.txt.tmp"
我更喜欢 FINDSTR - 它更可靠。例如,FIND 可以截断长行 - FINDSTR 不会,只要它直接从文件中读取。通过管道或重定向从标准输入读取时,FINDSTR 确实会删除长行。
如果文件可能包含以分隔符开头的行,那么您需要保留带有行号前缀的整行,然后使用搜索和替换来删除行前缀。将 %%A 传输到环境变量时,您可能希望延迟扩展关闭,否则任何!将被破坏。但稍后在循环中,您需要延迟扩展来进行搜索和替换。
::preserve blank lines using FIND, even if a line may start with ]
::long lines are truncated
for /f "delims=" %%A in ('type "file.txt" ^| find /n /v ""') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*]=!"
echo(!ln!
endlocal
)
::preserve blank lines using FINDSTR, even if a line may start with :
::long lines >8191 bytes are truncated
for /f "delims=*" %%A in ('type "file.txt" ^| findstr /n "^"') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
echo(!ln!
endlocal
)
::FINDSTR variant that preserves long lines
type "file.txt" >"file.txt.tmp"
for /f "delims=*" %%A in ('findstr /n "^" "file.txt.tmp"') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
echo(!ln!
endlocal
)
del "file.txt.tmp"
如果您不需要担心将文件转换为 ASCII,那么删除管道并让 FIND 或 FINDSTR 打开指定为参数的文件或通过重定向会更有效。
还有另一种解决方法可以在读取过程中完全绕过 FOR /F。它看起来很奇怪,但它更有效。使用延迟扩展没有任何限制,但不幸的是它还有其他限制。
1) 行必须由 终止(如果你进行 TYPE 文件转换,这不会有问题)
2) 行的长度必须 )
3) 任何尾随控制字符都会从每一行中去除。
4) 它必须从文件中读取——你不能使用管道。因此,在您的情况下,您将需要使用临时文件来进行 ASCII 转换。
setlocal enableDelayedExpansion
type "file.txt">"file.txt.tmp"
for /f %%N in ('find /c /v "" ^<"file.txt.tmp"') do set cnt=%%N
<"file.txt.tmp" (
for /l %%N in (1 1 %cnt%) do(
set "ln="
set /p "ln="
echo(!ln!
)
)
del "file.txt.tmp"