从CSV文件中删除非ASCII字符

69

我想要在原地从文件中删除所有的非ASCII字符。

我找到了一个使用tr命令的解决方案,但我猜我需要在修改后将该文件写回。

我需要在原地进行操作,并且性能相对较好。

有什么建议吗?


你能提供一个使用tr命令的一行代码的链接吗? - Jordan Sitkin
OP可能是指不可打印字符(ctrl-c,Unicode编号U+0002,是ASCII字符)。问题还应该指定区域设置 - 没有这些信息,人们可以(应该?)假设他指的是“C”区域设置。一个天真的答案是去除任何大于0x7f的字节 - 这将保留在C区域设置中不可打印但完全合法的ASCII字符。我因为这些原因而对问题进行了负分评价,这使得它过于模糊。 - Juan
11个回答

87
一个Perl的一行命令就可以完成:perl -i.bak -pe 's/[^[:ascii:]]//g' <你的文件> -i 表示文件将被直接编辑,备份文件将保存为 .bak扩展名。

1
这个也可以用于stdin作为输入。 - h3xStream
3
Perl的解决方案比sed的解决方案更快。使用sed尝试更新一个大小为122 GB的文件需要3个小时,而对我来说,perl只需要不到2个小时。 - user8128167
我在我的环境中(Ubuntu gnu sed 4.2.2)无法让 sed 解决方案正常工作,但这个解决方案非常好用。 - steve klein
1
尝试了所有方法,只有这个对我有效。一定要爱 Perl 的强大。谢谢! - jbrahy
然而,当试图用'?'替换非ASCII字符时,'??'会出现,我猜测是因为Perl替换了Unicode字符的两个字节,因此每个字节只有一个'?'。 $ echo "é" | perl -pe 's/[^[:ascii:]]/?/g' ?? - Hans Deragon

51
# -i (inplace)

sed -i 's/[\d128-\d255]//g' FILENAME

4
@Sujit:没有更好的解决方案。我只是想指出仍然会创建一个中间文件。有时这很重要。我只是不想让你误认为它真的在原地进行。 - Dennis Williamson
60
在GNU sed 4.2.1上,会输出"无效的排序字符"。 - Jason C
32
我可以使用 LANG=C sed -i 's/[\d128-\d255]//g' FILE 来避免“无效排序字符”错误。 - Patrick
1
@Patrick,那么你的设置有问题。C语言环境意味着7位字符,并且应该在该模式空间中生成该错误。我建议使用具有8位字符的语言环境,例如iso-8859-1。这对我有效。 - MarkI
2
我通过在sed调用前缀上LC_ALL=C来解决了“无效排序字符”错误。 - Diomidis Spinellis
显示剩余6条评论

41

19

尝试使用 tr 而不是 sed

tr -cd '[:print:]' < file.txt

6
OP明确提到他不想使用tr(因为他想要“原地”转换,而sed -i则伪装成这样的操作——实际上会在幕后写入一个临时文件并进行重命名)。因此,这个答案对OP没有帮助。但是,对于那些想要使用tr的人来说,你可能希望保留换行符(这里显示的20180228版本没有)。然而,一个简单的调整可以保留换行符和回车符: tr -cd '[:print:]\n\r' < file.txt - Juan
1
tr -cd '[:print:]' <file.txt | sponge file.txt - evandrix

16
sed -i 's/[^[:print:]]//' FILENAME

此外,它的作用类似于dos2unix


12
无效。[:print:]不同于ASCII。有许多可打印的非ASCII字符。 - Jason C
1
还有一个 g 修饰符缺失。只会删除第一个不可打印字符。 - proski
1
@JasonC 还有许多不可打印的ASCII字符。很可能原始问题表述不清。 - Juan

8
# -i (inplace)

LANG=C sed -i -E "s|[\d128-\d255]||g" /path/to/file(s)

LANG=C的作用是避免出现Invalid collation character错误。

这是基于Ivan的回答和Patrick的评论。


6

我正在使用一个非常基本的busybox系统,其中tr或POSIX字符类没有范围支持,因此我必须用老式的方式来完成它。以下是使用sed从文件中删除所有不可打印的非ASCII字符的解决方案:

sed -i 's/[^a-zA-Z 0-9`~!@#$%^&*()_+\[\]\\{}|;'\'':",.\/<>?]//g' FILE

1
我没有你的系统来测试它,但考虑到<SPACE>是字符32(十进制),波浪符“”是字符126,所有可打印的ASCII字符都在这两者之间。如果你的sed支持[a-z]类型的范围和[^ type "not in"语法,你应该能够用以下命令替换那个长字符串: `sed -i 's/[^ -]//g' FILE` (即/ [^ <SPACE> -~]/) - JohnGH
1
@JohnGH 很好,这确实有效!这是一个更好的解决方案,尽管已经过去了六年 :) - ACK_stoverflow
1
抱歉回复有点慢;-) - JohnGH

6
这是我的解决方案:
注:此处为html标签,请勿删除
sed -i 's/[^[:print:]]//g'

我在终端中仍然收到像007F这样的Unicode字符。 - Katastic Voyage
@KatasticVoyage 你的本地设置是什么(LANG,LC_CTYPE)? - Juan

3
作为sed或perl的替代品,您可以考虑使用ed(1)和POSIX字符类。
注意:ed(1)将整个文件读入内存以原地编辑,因此对于非常大的文件,您应该使用sed -i ...,perl -i ...。
# see:
# - http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed
# - http://en.wikipedia.org/wiki/Regular_expression#POSIX_character_classes

# test
echo $'aaa \177 bbb \200 \214 ccc \254 ddd\r\n' > testfile
ed -s testfile <<< $',l' 
ed -s testfile <<< $'H\ng/[^[:graph:][:space:][:cntrl:]]/s///g\nwq'
ed -s testfile <<< $',l'

3
awk '{ sub("[^a-zA-Z0-9\"!@#$%^&*|_\[](){}", ""); print }' MYinputfile.txt > pipe_out_to_CONVERTED_FILE.txt

这个答案缺少教育性的解释。 - mickmackusa

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