从文本文件中删除所有空格字符
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()