如何在SED中指定[任何符号]?

如何在SED中指定[任何符号]?

我正在使用 SED 命令从非常大的文本文件中去除外语和其他非键盘字符:

例子:

sed 's/[^a-zA-Z0-9]//g'

上面的命令保留仅包含字母数字字符的任何行,这接近我想要的。问题是它还会删除任何包含常见符号(如 !@#$% 等)的行。我想保留那些。我尝试搜索类似 !-) 之类的 bullion 命令。但我找不到类似的东西。

那么如何从列表中过滤掉阿拉伯语、俄语和无法输入的字符呢? (理想情况下,我不想仅仅对角色进行核攻击,我想对发现它的整行进行核攻击。)

答案1

除了像 Kusalananda 那样使用类之外,您还可以基于 unicode 创建自己的范围。查看这个参考 unicolde 表寻找您喜欢的角色。对于 PCRE,“标准”字符 + TAB 的可能方式是:

 grep -P '^[\x{0020}-\x{007e}\x{0008}]{1,}$' file

\x{000A}请注意,由于 的grep每行功能(在标准模式下),换行符不作为控制字符包含在内。请考虑 MS 风格的换行符会受到影响并用于\x{000d}\x{000a}换行符!

答案2

要从文本中删除非 ASCII 字符,请考虑使用tr如下所示:

LC_ALL=C tr -d -c '[:print:][:cntrl:]' <file.in >file.out

两个 POSIX 字符类[:print:][:cntrl:]一起涵盖 ASCII 范围内的所有字符,并且-c我们要求tr考虑对此的补集,即所有非 ASCII 字符。我们-d要求tr删除该补集中的字符。

我们设置LC_ALLC(或POSIX) 以使[:print:]字符类仅匹配 ASCII 范围 32 到 126 中的字符。否则它可能会匹配本地语言环境中的可打印字符,例如ä。该类[:cntrl:]匹配 0 到 31 和 127 范围内的字符。使用 时LC_ALL=C,这两个类一起涵盖 0 到 127,即 ASCII 字符。

要删除包含任何非 ASCII 字符的整行:

LC_ALL=C grep -v '[^[:print:][:cntrl:]]' <file.in >file.out

该表达式[^[:print:][:cntrl:]]将匹配单个非 ASCII 字符。随着-v我们的询问grep提取所有行不是匹配该表达式,即提取不包含任何非 ASCII 字符的行。

这两个命令也可以通过以下方式完成sed

删除非 ASCII 字符:

LC_ALL=C sed 's/[^[:print:][:cntrl:]]//g' <file.in >file.out

删除包含非 ASCII 字符的行:

LC_ALL=C sed '/[^[:print:][:cntrl:]]/d' <file.in >file.out

请注意,正如 Stéphane 指出的那样在评论中,上述命令将返回一个仅包含 ASCII 字符的文本,或至少编码为 ASCII 的字符(取决于文件的编码)。


一种完全不同的方法是使用iconv

iconv -c -t ascii file.in >file.out

这会将文件转换为 ASCII 编码,同时默默地删除所有无法转换的字符(不是行)。

相关内容