bat实现删除文本中的空行、空格、制表符、最后一行空行

发布时间:2024-03-10

准备一个old_file.txt文件,UTF-8编码,内容如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

first line

second line    

    third line    

 

    forth line      

         

    fifth line    

         

    sixth line    

         

    测试  1   

         

    测试  2   

Finish      

在Notepad++中,显示所有字符,截图看下


old_file.txt



二、通过for /f实现



1. 删除仅含换行符的空行

Windows系统下CR LF表示换行符


1

2

3

4

@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行仅含换行符的空白行被去除了。

注意:最后一行不含任何字符的保持原样


新旧文本对比




2. 删除所有空白行

删除仅含换行符、仅含空格或制表符的空白行


1

2

3

4

5

6

7

8

9

@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行)去除了。


删除所有空白行



3. 删除所有空白行以及非空白行左侧的空格和制表符

1

2

3

4

@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,则仅含空格或制表符的行,将输出为仅含换行符的空行。

执行结果:

新旧文本对比,可以看出所有空白行及非空白行左侧的空格或制表符都去掉了


删除空白行及左侧空白



4. 删除所有空白行以及非空白行两侧的空格和制表符

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

@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=*"已经把左侧的去除了)

执行结果:

新旧文本对比,可以看出所有空白行、非空白行两侧的空格和制表符都去掉了,中间的空格和制表符仍保留


删除空白行及非空白行两侧的空格和制表符



5. 删除所有空白行以及所有空格和制表符

1

2

3

4

5

6

7

8

9

@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实现

1. 删除仅含换行符的空行

1

2

3

4

@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 字位置: 字的开始

xyz\> 字位置: 字的结束

/c:string 使用指定字符串作为文字搜索字符串

findstr . 表示包含任何字符的行,因此除第4行,其余都满足

执行结果:

新旧文本对比,可以看出第4行仅含换行符的空白行被去除了


findstr删除空行


2. 删除所有空白行

删除仅含换行符、仅含空格或制表符的空白行


1

2

@echo off

findstr /v /r /c:"^[    ]*$" old_file.txt >new_file7.txt

Tips:


"^[ ]*$" 的[]内是<space> <tab>两个字符,这个表达式的意思是:从行首到行尾,仅有0个或多个空格或制表符;

执行结果:

新旧文本对比,可以看出仅含换行符、仅含空格或指标符的空白行(第4、6、8、10、12行)去除了。


删除所有空白行


无法通过 findstr 的方式,实现删除非空白行两侧的空格和制表符



四、删除文件最后一行空行

我这里有个需求将old_file.txt文件中仅含换行符的空行及最后一行去掉,文件内容如下

(这里只给截图,准备数据里有,只是最后一行有区别)


在这里插入图片描述


然后你再使用【二、通过for /f实现】中的批处理脚本处理修改后的old_file.txt文件,你会发现新生成的文件Finish....后边始终会多出来一个CR LF换行符,其实这是因为 echo在输出内容时会自动在行尾尾随一个CR LF,大多数情况下这也没啥影响,但假如(无论原文件中是否有最后一行空行)你就想在新生成的文件中去掉它,该如何处理呢?


尝试了几种方法,总结如下:



1. 使用set /p=实现

代码来源:batfile - how to remove the last, empty, line in a file?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@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


删除效果



2. 使用backspace回退字符实现

为了解决set /p=导致的,前导空格或制表符剥离的问题,有大神提出了,使用backspace回退字符的方案


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

@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文件,看下效果。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@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字符


输出结果对比


我也尝试了


1

<nul set /p "_s=.◘%%L"

参考:

◘是一个backspace字符,在不同的CodePage下可能显示不同,测试效果同上,也仅在dos窗口上有效,重定向到文件后无效,这里不再演示了



3. 使用echo+set /p=实现

1

2

3

4

5

6

7

8

@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抑制了最后一行尾随的换行符,所以最后不会出现一个空行。

执行结果:

可以发现,已经能满足我的使用场景:删除仅含换行符的空行以及最后一行空行


结果对比



4. 使用prompt+cmd /d /k <nul实现

然后我又在 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?


1

2

3

4

5

6

7

8

9

10

11

12

@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

    )

)

执行结果:

可以发现,已经能满足我的使用场景:删除仅含换行符的空行以及最后一行空行


执行结果对比



五、总结

【四、删除文件最后一行空行】中 1、2 都不满足我的需求;3、4 可以满足我的需求;当然使用batch实现删除文件最后一行空行的方式肯定还有,文末列出的参考资料里也有很多其他方案可供参考,也建议仔细阅读,相信你会有不少收获。

findstr实现的【1. 删除仅含换行符的空行】【2. 删除所有空白行】从效率和安全性上都比for /f实现的要好很多。


注册即送1000元现金券