这篇文章将演示 bat批处理如何删除文件中空白行、空格、制表符
准备一个old_file.txt
文件,UTF-8编码,内容如下:
first line
second line
third line
forth line
fifth line
sixth line
测试 1
测试 2
Finish
for /f
实现Windows系统下CR
LF
表示换行符
@echo off
for /F "delims=" %%l in (old_file.txt) do (
echo.%%l>>new_file1.txt
)
Tips:
echo.%%l>>
,.
不能省略,%%l
与>>
之间不能有空格,不然空格会在新结果中出现;echo.
你可以替换成echo+
、echo;
、echo:
等等,关于echo这种用法的更多讨论可参考:ECHO. FAILS to give text or blank line - Instead use ECHO/
执行结果:
新旧文本对比(肉眼对比),可以看出第4行仅含换行符的空白行被去除了。
注意:最后一行不含任何字符的保持原样
删除仅含换行符、仅含空格或制表符的空白行
@echo off
setlocal enabledelayedexpansion
for /F "delims=" %%L in (old_file.txt) do (
set "str=%%L"
set "str1=!str: = !"
set "str2=!str1: =!"
if not "!str2!"=="" echo.%%L
)>>new_file2.txt
endlocal
Tips:
set "str1=!str: = !"
,注意这里的被替换字符是一个制表符
,将制表符
替换为空格
(尝试了将制表符
替换为空,会出现第8行两个指标符的行仍原样输出了。。。不知道为啥);set "str2=!str1: =!"
,将空格
替换为空;- 经过上边两次替换,如果仍不为空,则表示该行为非空白行,然后
echo.%%L
输出原行内容
执行结果:
新旧文本对比,可以看出仅含换行符、仅含空格或指标符的空白行(第4、6、8、10、12行)去除了。
@echo off
for /F "tokens=*" %%L in (old_file.txt) do (
if not "%%L"=="" echo.%%L
)>>new_file3.txt
Tips:
if not "%%L"=="" echo.%%L
如果换成echo.%%L
,则仅含空格或制表符的行,将输出为仅含换行符的空行。
执行结果:
新旧文本对比,可以看出所有空白行及非空白行左侧的空格或制表符都去掉了
@echo off
setlocal enabledelayedexpansion
for /F "tokens=*" %%L in (old_file.txt) do (
set "str=%%L"
call :rdel
if not "!str!"=="" echo !str!>>new_file4.txt
)
endlocal
goto :eof
:rdel
set tmp=%str%
:sloop
if "%tmp%"=="" goto eloop
if "%tmp:~-1%"==" " (
set "tmp=%tmp:~0,-1%"
goto sloop
)
if "%tmp:~-1%"==" " (
set "tmp=%tmp:~0,-1%"
goto sloop
)
:eloop
set "str=%tmp%"
goto :eof
Tips:
- 定义了一个
rdel
函数,用来去除非空白行右侧的空格和制表符(为什么未写去除左侧的空格和制表符,因为for /F "tokens=*"
已经把左侧的去除了)
执行结果:
新旧文本对比,可以看出所有空白行、非空白行两侧的空格和制表符都去掉了,中间的空格和制表符仍保留
@echo off
setlocal enabledelayedexpansion
for /F "delims=" %%L in (old_file.txt) do (
set "str=%%L"
set "str1=!str: = !"
set "str2=!str1: =!"
if not "!str2!"=="" echo.!str2!
)>>new_file5.txt
endlocal
Tips:
- 其实就是将【2. 删除所有空白行】的代码中
echo.%%L
换成了echo.!str2!
执行结果:
新旧文本对比,可以看出所有空白行、所有空格和制表符(包括文字中间的)都去掉了
findstr
实现@echo off
findstr /v /r /c:"^$" old_file.txt > new_file6.txt
::或者
::findstr . old_file.txt > new_file6.txt
Tips:
/v
仅打印不包含匹配项的行;/r
将搜索字符串作为一般表达式使用;
一般表达式快速参考:
表达式 解释说明 .
通配符: 任何字符 *
重复: 以前字符或类出现零或零以上次数 ^
行位置: 行的开始 $
行位置: 行的终点 [class]
字符类: 任何在字符集中的字符 [^class]
补字符类: 任何不在字符集中的字符 [x-y]
范围: 在指定范围内的任何字符 \x
Escape: 元字符 x 的文字用法 \
字位置: 字的开始 xyz\>
字位置: 字的结束
/c:string
使用指定字符串作为文字搜索字符串findstr .
表示包含任何字符的行,因此除第4行,其余都满足
执行结果:
新旧文本对比,可以看出第4行仅含换行符的空白行被去除了
删除仅含换行符、仅含空格或制表符的空白行
@echo off
findstr /v /r /c:"^[ ]*$" old_file.txt >new_file7.txt
Tips:
"^[ ]*$"
的[]
内是
两个字符,这个表达式的意思是:从行首到行尾,仅有0个或多个空格或制表符;
执行结果:
新旧文本对比,可以看出仅含换行符、仅含空格或指标符的空白行(第4、6、8、10、12行)去除了。
无法通过 findstr 的方式,实现删除非空白行两侧的空格和制表符
我这里有个需求将old_file.txt
文件中仅含换行符的空行及最后一行去掉,文件内容如下
(这里只给截图,准备数据里有,只是最后一行有区别)
然后你再使用【二、通过for /f
实现】中的批处理脚本处理修改后的old_file.txt
文件,你会发现新生成的文件Finish....
后边始终会多出来一个CR LF
换行符,其实这是因为 echo
在输出内容时会自动在行尾尾随一个CR LF
,大多数情况下这也没啥影响,但假如(无论原文件中是否有最后一行空行)你就想在新生成的文件中去掉它,该如何处理呢?
尝试了几种方法,总结如下:
set /p=
实现代码来源:batfile - how to remove the last, empty, line in a file?
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FLAG="
> "new_file.txt" (
for /F "usebackq delims=" %%L in ("old_file.txt") do (
if defined FLAG echo.
::echo.|set /P "dummyName=%%L"
< nul set /P "dummyName=%%L"
set "FLAG=#"
)
)
endlocal
exit /B
执行结果:
新旧文本对比,你会发现,最后一行不再有CR LF
,但是每行的开头的空格或制表符也一并给去掉了。但我这个脚本最初的意图删除仅含换行符的空行以及最后一行空行。
这是因为set /p =
会使 前导引号
或空格
剥离,前导=
报错语法错误,在不同的Windows版本之间限制会有不同,有关详细讨论参见:
SET /P prompt mechanics - New behavior: = makes syntax error
backspace
回退字符实现为了解决set /p=
导致的,前导空格或制表符剥离的问题,有大神提出了,使用backspace
回退字符的方案
@echo off
chcp 65001 & cls
setlocal EnableExtensions DisableDelayedExpansion
::获取回退字符并存储到bs变量
for /F %%a in ('"prompt $H & for %%b in (1) do rem"') do (set "bs=%%a")
::处理文本
set "FLAG="
(
for /F "usebackq delims=" %%L in ("old_file.txt") do (
if defined FLAG echo.
< nul set /P "dummyName=+%bs%%%L"
set "FLAG=#"
)
)
endlocal
pause >nul
上边代码仅输出到dos窗口,先来看下在dos窗口的输出效果
对比原文本可以看出,对于tab
制表符作为前导的行,backspace
并未生效,其余的生效了。
然后代码做下改动,输出到new_file.txt
文件,看下效果。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
::获取回退字符并存储到bs变量
for /F %%a in ('"prompt $H & for %%b in (1) do rem"') do (set "bs=%%a")
::处理文本
set "FLAG="
> "new_file.txt" (
for /F "usebackq delims=" %%L in ("old_file.txt") do (
if defined FLAG echo.
< nul set /P "dummyName=.%bs%%%L"
set "FLAG=#"
)
)
endlocal
exit /B
看下输出结果对比,可以发现backspace
回退字符,在重定向到文件后,完全无效,+
未被回退,还多出来一个BS
字符
我也尝试了
<nul set /p "_s=.◘%%L"
参考:
◘
是一个backspace
字符,在不同的CodePage
下可能显示不同,测试效果同上,也仅在dos窗口上有效,重定向到文件后无效,这里不再演示了
echo
+set /p=
实现参考 使用批处理从文本文件中删除最后一行(包括空行),我针对我的场景,进行了优化代码如下:
@echo off
for /F "delims=" %%L in (old_file.txt) do (
setlocal enabledelayedexpansion
if defined row echo.!row!
endlocal
set row=%%L
)>>new_file1.txt
echo.|set /p "=%row%" >>new_file1.txt
Tips:
for /f
部分使用echo
输出除最后一行外的内容,echo.|set /p
部分负责输出最后一行,set /p
抑制了最后一行尾随的换行符,所以最后不会出现一个空行。
执行结果:
可以发现,已经能满足我的使用场景:删除仅含换行符的空行以及最后一行空行
prompt
+cmd /d /k 实现
然后我又在 Output text without linefeed, even with leading space or = 评论区里看到了jeb
大神给出的set "prompt=[promptString]"
+cmd /d /k < nul
的方案。
不过这里的promptString
最长为511字符,你也可以到这里看相关说明How do I add a space on this line?
@echo off
set flag=
> new_file.txt (
for /F "delims=" %%L in (old_file.txt) do (
setlocal
if defined flag echo.
set "prompt=%%L"
cmd /d /k < nul
endlocal
set flag=T
)
)
执行结果:
可以发现,已经能满足我的使用场景:删除仅含换行符的空行以及最后一行空行
findstr
实现的【1. 删除仅含换行符的空行】【2. 删除所有空白行】从效率和安全性上都比for /f
实现的要好很多。参考资料:
Windows Batch: How remove all blank (or empty) lines
Batch: remove all white spaces from a text file?
batfile - how to remove the last, empty, line in a file?
Windows batch: echo without new line
Output text without linefeed, even with leading space or =
How do I add a space on this line?