Contents

从文本文件中删除所有空格字符

1. 概述

有时,我们可能需要删除空格字符来清理某些文件的内容。我们可以从 Linux 命令行通过几种不同的方式来执行此操作。

在本教程中,我们将介绍如何删除所有类型的空格,包括 Unicode。我们还将了解如何单独管理换行符。

2. 问题介绍

2.1. 什么是空白字符?

空格 通常是可打印字符之间的间距。这可以在一条线内(水平)或分隔线(垂直)。

有时,我们想从文件中删除所有空白字符。但是,我们经常面临只删除水平空白字符的要求。换句话说,我们可能希望从文件的每一行中删除所有空格,但仍将它们保留为单独的行。

在本教程中,我们将探讨这两种情况。

我们还应该注意,[Unicode 字符集](https://en.wikipedia.org/wiki/Template:Whitespace_(Unicode) 定义了一些额外的空白字符,例如,垂直制表符 ( U+000B ) 字符和“图形空格”( U+2007 ) 字符。

2.2. 输入示例

让我们从一个水平和垂直空白的例子开始:

$ cat -n raw_file.txt
     1	     We Have Leading Spaces.
     2	Now We Have Two Tabs:		And An Empty Line:
     3	
     4	And We Have A Couple Of Trailing Blank Lines:
     5		      
     6		 

在这里,我们使用了带有-n*选项的cat 命令来打印带有行号的文件内容*。这样,我们可以清楚地看到输出中的空行。

如上面的输出所示,我们的 raw_file.txt包含不同的空白字符,例如空格、制表符和换行符。我们的目标是将它们全部删除。

在本教程中,我们将查看一些命令:

这些很常见,应该在大多数 Linux 发行版中都可以找到。

3. 使用 tr命令

tr 命令从标准输入 ( stdin )读取字节流, 翻译或删除字符,然后将结果写入标准输出 (stdout)。

我们可以使用tr命令的*-d*选项(用于删除特定字符 )来删除空白字符。语法是:tr -d SET1

因此,根据要求,将正确的SET1字符传递给tr成为仅删除水平空白字符或所有空白字符的关键。

3.1. 仅删除水平空格

首先,让我们从输入文件中删除所有水平空格。** tr为所有水平空白定义了“ [:blank:] ”字符集**。

另外,我们应该记住tr命令只从stdin读取数据。因此,我们需要将raw_file.txt的内容重定向到stdin

$ tr -d "[:blank:]" < raw_file.txt | cat -n
     1	WeHaveLeadingSpaces.
     2	NowWeHaveTwoTabs:AndAnEmptyLine:
     3	
     4	AndWeHaveACoupleOfTrailingBlankLines:
     5	
     6  

在示例中,我们还将tr的结果通过管道传输到cat -n以验证空行。

因此,正如输出所示,我们删除了所有水平空格,但保留了换行符。

3.2. 删除所有空白字符

接下来,让我们从文件中删除所有空白字符。 [:space:] ”字符集表示所有水平和垂直空白

$ tr -d "[:space:]" < raw_file.txt
WeHaveLeadingSpaces.NowWeHaveTwoTabs:AndAnEmptyLine:AndWeHaveACoupleOfTrailingBlankLines:

在这里,我们不需要将输出通过管道传送到cat以查看没有换行符!

4. 使用 sed命令

sed是一种广泛使用的非交互式流编辑实用程序。

4.1. 仅删除水平空格

首先,让我们删除所有水平空白字符。[:blank:] ”也是一个 POSIX 标准字符类,代表水平空白

sed适用于正则表达式。要在正则表达式中使用这个字符类,它变成“ [[:blank:]] ”:

$ sed 's/[[:blank:]]//g' raw_file.txt | cat -n
     1	WeHaveLeadingSpaces.
     2	NowWeHaveTwoTabs:AndAnEmptyLine:
     3	
     4	AndWeHaveACoupleOfTrailingBlankLines:
     5	
     6	

4.2. 删除所有空白字符

类似地,“ [:space:] ”是用于水平和垂直空白的 POSIX 标准字符类。

但是,与tr命令不同,我们不能在*sed命令中将**[[:blank:]]替换为[[:space:]]*以删除所有空格。

**默认情况下,sed命令逐行读取、处理和输出。**当它写入输出时,如果模式空间不以换行符结尾,它会自动将换行符附加到当前模式空间。

因此,即使我们将*[:space:]替换为空,当sed*输出该行时,换行符也会返回。

如果我们希望sed删除垂直空格,例如换行符,我们需要告诉sed继续读取和删除空格,直到文件末尾,然后只输出一次

$ sed ':a; N; s/[[:space:]]//g; ta' raw_file.txt
WeHaveLeadingSpaces.NowWeHaveTwoTabs:AndAnEmptyLine:AndWeHaveACoupleOfTrailingBlankLines:

4.3. 了解sed命令

上面的sed命令非常紧凑。然而,理解起来可能并不那么简单。让我们快速分解一下,看看它是如何工作的:

  • :一个; ——这不是命令。它只是定义了一个名为“ a ”的标签
  • N; – 将下一行添加到模式空间 中。
  • s/[[:space:]]//g; – 和以前一样,s命令从当前模式空间中的文本中删除所有空格。
  • ta - 这个分支 sed回到标签“ a ”。

sed命令中,:a …. ta ’ 像循环一样工作。当我们通过N向模式空间添加新行时;命令,当然,我们至少有一个空格——换行符。因此,sed将继续追加下一行并删除空白字符,直到文件中的最后一行。

当涉及到输入文件的末尾时,N; 命令检测 EOF。因此,sed将在模式空间中输出当前结果并终止处理。 这样,sed已经从输入文件中删除了所有空白字符,包括换行符。

许多 sed实现支持将结果写回输入文件。例如,广泛使用的GNU Sed 提供了*-i* 选项来进行“就地”更改

5. 使用awk 命令

awk是另一个强大的文本处理实用程序。它定义了自己的类 C 脚本和大量的内置变量和函数来灵活地操作处理。

5.1.仅删除水平空格

awk也支持正则表达式。因此,awk命令完全支持 POSIX 标准字符类,例如*[:blank:][:space:]。 我们可以调用gsub* 函数来删除所有水平空格:

$ awk '{gsub(/[[:blank:]]/,""); print}' raw_file.txt | cat -n          
     1	WeHaveLeadingSpaces.
     2	NowWeHaveTwoTabs:AndAnEmptyLine:
     3	
     4	AndWeHaveACoupleOfTrailingBlankLines:
     5	
     6	

如上面的输出所示,我们已经解决了这个问题。

5.2. 删除所有空白字符

sed类似,默认情况下,awk也逐行读取、处理和输出。

awk打印记录时,它通过内置的ORS 变量将它们分开。ORS变量的默认值为一个换行符。

因此,我们可以对上面的awk命令进行两次修改,要求它删除所有空格,包括换行符:

  • 用*[:space:]*替换字符类
  • 设置一个空字符作为ORS变量的值

接下来,让我们看看它的实际效果:

$ awk -v ORS="" '{gsub(/[[:space:]]/,""); print}' raw_file.txt | cat -n
     1	WeHaveLeadingSpaces.NowWeHaveTwoTabs:AndAnEmptyLine:AndWeHaveACoupleOfTrailingBlankLines:

6. Unicode 空格

到目前为止,我们已经学习了几种从输入文件中删除空白字符的方法。这些解决方案适用于所有 ASCII 文本文件。 在我们的日常工作中,我们需要使用的大多数文本文件都是 ASCII 文本文件。但是,空格包含非 ASCII Unicode 字符

现在,让我们讨论 Unicode 字符的处理。我们假设我们的默认语言环境是en_US.utf-8

6.1. 输入示例

首先,让我们看一个包含非 ASCII Unicode 字符的输入文件:

$ cat raw_unicode.txt
Some Non-whitespace Unicode Characters:
[Check Mark]: U+2714 ()
[Cross Mark]: U+2716 ()
Some Unicode Whitespace Characters:
[Figure Space]: U+2007 ()
[Thin Space]: U+2009 ()
[Paragraph Separator]: U+2029 ()
[Ideographic Space]: U+3000 ( )

在这个文件中,我们有六个 Unicode 字符,格式如下:[名称]: Code_In_Hex (The Character)

现在,让我们尝试使用我们的tr解决方案从这个raw_unicode.txt文件中删除水平空格:

$ tr -d "[:blank:]" < raw_unicode.txt
SomeNon-whitespaceUnicodeCharacters:
[CheckMark]:U+2714()
[CrossMark]:U+2716()
SomeUnicodeWhitespaceCharacters:
[FigureSpace]:U+2007()
[ThinSpace]:U+2009()
[ParagraphSeparator]:U+2029()
[IdeographicSpace]:U+3000( )

如输出所示,已删除所有 ASCII 空格,例如空格。但是,括号中的非 ASCII Unicode 空格仍然存在。

这说明当文件包含 Unicode 字符时,情况会有所不同。在 Linux 中处理 Unicode 文件时,我们测试过的命令或脚本突然不再工作是很常见的。

因此,在我们专注于删除 Unicode 空格之前,有必要测试一下我们的文件是否包含 Unicode 字符。

6.2. 提示:检查文本文件中的 Unicode 字符

首先,我们可以使用file 命令来测试一个文本文件是包含 ASCII 还是 Unicode:

$ file raw_file.txt 
raw_file.txt: ASCII text
$ file raw_unicode.txt 
raw_unicode.txt: Unicode text, UTF-8 text

输出向我们展示了哪个文件包含 Unicode 字符。

因此,在实践中,如果我们的脚本突然无法处理特定文件,我们可能首先要检查该文件是否包含 Unicode 字符。

6.3. 删除 Unicode 空格

不幸的是,没有标准的字符类来匹配所有的 Unicode 空格。但是,总共只有大约 20 个具有属性[white_space=yes](https://en.wikipedia.org/wiki/Template:Whitespace_(Unicode) 的Unicode 字符。

因此,我们可以构建自己的“字符类”来包含所有这些字符:

SPACES=$(printf "%b" "\U00A0\U1680\U2000\U2001\U2002\U2003\U2004\U2005\U2006\U2007\U2008\U2009\U200A\U2028\U2029\U202F\U205F\U3000")

如上面的语句所示,我们将所有 Unicode 空格保存在一个名为*$SPACES*的 shell 变量中。

然后,如果我们想删除所有 Unicode 空格,我们可以构建一个正则表达式字符类*“[$SPACES]”*来进行替换。

接下来,让我们使用sed命令从raw_unicode.txt文件中 删除所有水平空格,包括非 ASCII 空格:

$ sed "s/[[:blank:]$SPACES]//g" raw_unicode.txt 
SomeNon-whitespaceUnicodeCharacters:
[CheckMark]:U+2714()
[CrossMark]:U+2716()
SomeUnicodeWhitespaceCharacters:
[FigureSpace]:U+2007()
[ThinSpace]:U+2009()
[ParagraphSeparator]:U+2029()
[IdeographicSpace]:U+3000()

正如我们在上面的输出中看到的,sed命令已经删除了所有水平空格,包括括号中的那些非 ASCII 空格。此外,Unicode 字符“✔”和“✖”仍然存在。

最后,让我们看另一个使用awk命令从文件中删除所有空格的示例:

$ awk -v ORS="" -v uspaces="$SPACES" '{gsub("[[:space:]"uspaces"]",""); print}' raw_unicode.txt 
SomeNon-whitespaceUnicodeCharacters:[CheckMark]:U+2714()[CrossMark]:U+2716()SomeUnicodeWhitespaceCharacters:[FigureSpace]:U+2007()[ThinSpace]:U+2009()[ParagraphSeparator]:U+2029()[IdeographicSpace]:U+3000()