Bash - 删除所有Unicode空格并替换为普通空格

6
我有一个文件包含大量文本,其中夹杂着特殊的空格字符,这些字符属于Unicode Spaces
我需要将它们全部替换为普通的"space"字符。
4个回答

9

使用 Perl 很容易:

perl -CSDA -plE 's/\s/ /g' file

但是正如@mklement0在评论中正确指出的那样,它也将匹配\t(制表符)。如果这是个问题,您可以使用:

perl -CSDA -plE 's/[^\S\t]/ /g'

演示:

X             X

以上内容包括:

U+00058 X LATIN CAPITAL LETTER X
U+01680   OGHAM SPACE MARK
U+02002   EN SPACE
U+02003   EM SPACE
U+02004   THREE-PER-EM SPACE
U+02005   FOUR-PER-EM SPACE
U+02006   SIX-PER-EM SPACE
U+02007   FIGURE SPACE
U+02008   PUNCTUATION SPACE
U+02009   THIN SPACE
U+0200A   HAIR SPACE
U+0202F   NARROW NO-BREAK SPACE
U+0205F   MEDIUM MATHEMATICAL SPACE
U+03000   IDEOGRAPHIC SPACE
U+00058 X LATIN CAPITAL LETTER X

使用:

perl -CSDA -plE 's/\s/_/g'  <<<"X             X"

注意,对于演示替换为下划线,打印出来

X_____________X

同时,也可以使用纯bash实现。

LC_ALL=en_US.UTF-8 spaces=$(printf "%b" "\U00A0\U1680\U180E\U2000\U2001\U2002\U2003\U2004\U2005\U2006\U2007\U2008\U2009\U200A\U200B\U202F\U205F\U3000\UFEFF")

while read -r line; do
    echo "${line//[$spaces]/ }"
done

LC_ALL=en_US.UTF-8 只有在你的默认语言环境不是 UTF-8 时才需要设置。(如果你正在处理 utf8 文本,则应该使用它):)

str="X             X"
echo "${str//[$spaces]/_}"

再次打印:

X_____________X

同样地,使用sed - 准备与上述相同的变量$spaces并使用以下命令:

sed "s/[$spaces]/ /g" file

编辑 - 因为一些奇怪的复制/粘贴(或本地化)问题:

xxd -ps <<<"$spaces"

展示

c2a0e19a80e1a08ee28080e28081e28082e28083e28084e28085e28086e2
8087e28088e28089e2808ae2808be280afe2819fe38080efbbbf0a

md5摘要(两个不同的程序)

md5sum <<<"$spaces"
LC_ALL=C md5 <<<"$spaces"

打印相同的md5

35cf5e1d7a5f512031d18f3d2ec6612f  -
35cf5e1d7a5f512031d18f3d2ec6612f

1
纯 Bash 版本有问题:在我的终端中,它会删除所有大写字母“A”。如果我使用 Python spaces=$(python -c 'print u"\u0020\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200b\u202f\u205f\u3000\ufeff".encode("utf8")') 就可以正常工作。 - Kuzeko
简单的复制和粘贴检查:md5sum <<<"$spaces" 的结果应该打印出 35cf5e1d7a5f512031d18f3d2ec6612f,如果不是,则说明你的复制和粘贴有误。 - clt60
1
但是...你是对的-区域设置会影响printf本身。你需要有xx_xx.UTF-8本地化设置,例如en_US.utf8。已编辑答案。 - clt60
2
做得好;值得注意的事情:使用\U...转义需要Bash 4.x;关于perl解决方案:\s也会匹配_tab_,并将其替换为单个空格可能不是所需的。一个小问题:printf "%b" '...'可以直接写成printf '...'- 不需要%b这一步骤。 - mklement0
2
@mklement0 是的,我忘记了\t。关于%b也是对的,但我个人喜欢显式格式。;) 感谢您的评论。添加关于\t的编辑。 - clt60
显示剩余5条评论

3

可以通过它们的Unicode来识别字符,但是sed 's/[[:space:]]\+/\ /g'无法实现这一点。

通过改编另一个SO答案,我们列出所有的Unicode并将它们保存在一个变量中,然后使用sed进行替换(注意使用-i.bak,我们还将保存原始文件的副本)。

 CHARS=$(printf "%b" "\U00A0\U1680\U180E\U2000\U2001\U2002\U2003\U2004\U2005\U2006\U2007\U2008\U2009\U200A\U200B\U202F\U205F\U3000\UFEFF")

 sed -i.bak 's/['"$CHARS"']/ /g' /tmp/file_to_edit.txt 

1
干得好;值得注意的事情:使用\U...转义需要Bash 4.x。 如果您的sed(a)遵循区域设置的UTF-8字符映射,并且(b)该字符映射是最新的(例如,Ubuntu 16.04不是这种情况),那么s/[[:space:]]/ /g'应该可以工作-它也会替换制表符。 \+并不是必要的,更重要的是,它不可移植。 小问题:printf "%b" '...'可以改为printf '...'-不需要%b的绕路; 最好不要使用全大写的Shell变量名称。 - mklement0
1
感谢您的评论,随意编辑答案并添加一个代码块,展示您更加“稳定”的解决方案。我本人也可以这样做,但我不能百分之百地理解所有建议的修改。 - Kuzeko

1
如果你经常面临这个任务,请考虑安装nws(标准化空白),这是一个简化任务的实用程序(由我提供):
nws --ascii file # convert non-ASCII whitespace and punctuation to ASCII

nws --ascii -i file  # update file in place
< p > nws--ascii 模式:

  • 将(非ASCII)Unicode 空格(例如不间断空格( ))和标点符号(例如弯引号(“”),短横线(),...)转换为最接近的 ASCII 等效物。

  • 同时保留其他任何 Unicode 字符。

此模式对于已使用印刷引号、破折号等格式进行显示的源代码示例非常有帮助,这通常会使编译器/解释器无法处理代码。


安装nws(适用于Linux和macOS)从npm registry。请注意:即使您不使用Node.js,其包管理器npm也可以跨平台工作并且易于安装;尝试curl -L https://git.io/n-install | bash。已安装Node.js,请按照以下步骤进行安装:
[sudo] npm install nws-cli -g

注意:

  • 您是否需要使用sudo取决于您如何安装Node.js以及是否稍后更改了权限;如果出现EACCES错误,请尝试使用sudo再次运行。
  • -g确保全局安装,并且需要将nws-cli放入系统的$PATH中。

手动安装(任何带有bash的Unix平台)

  • 下载this bash脚本并将其命名为nws
  • 使用chmod +x nws将其变为可执行文件。
  • 将其移动或创建符号链接到您的$PATH中的文件夹,例如在(macOS)中的/usr/local/bin或(Linux)中的/usr/bin

可选阅读:POSIX字符类[:space:][:blank:]以及非ASCII Unicode空格

在基于UTF-8的语言环境中,符合POSIX标准的工具应该使POSIX字符类[:space:][:blank:]匹配(非ASCII)Unicode空格

这依赖于区域设置字符映射正确分类基于 POSIX规定的字符分类的Unicode字符,这些直接对应于模式和正则表达式中可用的字符类,如[:space:]

有两个陷阱

  • Unicode 是一个不断发展的标准(截至本文版本为第9版);你所使用的 UTF-8 字符映射表可能已经过时

    • 例如,在 Ubuntu 16.04 上,以下字符未被正确分类,因此无法匹配 [:space:] / [:blank:]
      不换行空格、数字空格、窄不换行空格、下一行
  • 工具应该使用活动区域设置的字符映射表 - 但有令人遗憾的例外 - 以下工具不支持 Unicode(可能还有更多)

    • 在 GNU 工具中(截至 coreutils v8.27):

      • cuttr
    • Mawk,例如 Ubuntu 默认的 awk 实现。

    • 在 BSD/macOS 工具中(截至 macOS 10.12):

      • awk
因此,在当前使用UTF-8字符集的平台上,以下sed命令应该可以工作,但请注意[:space:]也匹配制表符,因此也会将它们替换为一个空格:
sed 's/[[:space:]]/ /g' file

0
如果你使用python3,这个代码对我有用,虽然是临时凑出来的,但确实可用。
FILENAME = 'File.txt'
OUTPUTNAME = 'Fixed.txt'
f = open(FILENAME, 'r+', encoding='utf8')
o = open(OUTPUTNAME, 'w+', encoding='utf8')
for line in f:
    for ch in line:
        if ch == '\u2003':
            ch = ' '
            o.write(ch)
        else:
            o.write(ch)
o.close()
f.close()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接