从工作流中删除所有非ASCII字符(文件)


13

如何从一个文件中删除所有非ASCII字符?是否会有特定的命令来执行此操作?

grep --colour='auto' -P -n'[^\x00-\x7]' /usr/local/...

我相信这可以在工作流程中找到字符,但是如何删除有问题的字符的所有实例?



2
相关:如果您只是想避免控制字符的问题(而不是无声地摆脱它们),则可以简单地使用cat -vASCII表示形式来显示它们。(例如,^G用于\007
Matija NALIS

1
当您说“非ASCII字符”时,您还包括带重音符号的字符吗?
曼队长

1
@MatijaNalis有关表示的更多信息:en.wikipedia.org/wiki/Caret_notation
wjandrea

1
用例是什么?通常,有一些特定的工具或不同的方法比仅删除一些特殊字符要好得多。请注意,ASCII确实包含几个“特殊”字符,例如垂直制表符,贝尔和NUL-您确定不是可打印字符吗?
l0b0

Answers:


26

ASCII字符是介于0到177(八进制)之间的字符

要删除文件中超出此范围的字符,请使用

LC_ALL=C tr -dc '\0-\177' <file >newfile

tr命令是一个适用于单个字符的实用程序,可以用其他单个字符替换它们(音译),删除它们或将相同字符的行程压缩为单个字符。

上面的命令将从中读取file修改的内容并将其写入newfile。该-d选项tr品牌(而不是他们音译)实用程序删除字符,-c使得它考虑给定的时间间隔(内代替)之外的字符。

LC_ALL=C确保每个字节值组成一个有效字符。没有它,tr如果某些实现在语言环境的字符编码中发现未形成有效字符的字节序列,它们将中止。


要将原始文件替换为修改后的文件,请使用

LC_ALL=C tr -dc '\0-\177' <file >newfile &&
mv newfile file

tr成功完成后,这会将新文件重命名为旧文件的名称。如果tr由于无法读取原始文件或无法写入新文件而未能成功完成,则原始文件将保持不变。

或者,要保留尽可能多的原始文件的元数据(权限等),请使用

cp file tmpfile &&
LC_ALL=C tr -dc '\0-\177' <tmpfile >file &&
rm tmpfile


9

如果您只需要一个正则表达式:[\x00-\x7F]您可以将其应用于多个实用程序:

<file LC_ALL=C   sed   's/[^\o0-\o177]//g'      # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^\0-\177]/,"");print}'
<file            perl  -pe 's/[^[:ascii:]]//g;'
<file LC_ALL=C   tr    -dc '\0-\177'

了解sed,awk和perl期望使用Unix中定义的“文本文件”。在这种情况下,所有人都可以正常工作。但是具体来说,awk添加了结尾的换行符(无论它是否存在于源文件中)(使用printf会删除输入中的所有换行符)。tr旨在与任何文件类型一起使用。但是NUL(\0)在POSIX文本文件中不是有效字符,应避免使用:

这些行不包含NUL字符...

实际上,许多控制字符在某些特定条件下还会产生其他问题。
所以,可能你需要[\x07-\x0d\x20-\x7e]

<file LC_ALL=C   sed   's/[^\o007-\o015\o040-\o176]//g'            # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^\0-\15\40-\176]/,"");print}'
<file            perl  -pe 's/[^\x{7}-\x{d}\x{20}-\x{7e}]//g;'
<file LC_ALL=C   tr    -dc '\7-\15\40-\176'

范围7-13(十进制)是\a\b\t\n\v\f\r(按顺序)。
类似的(可能更便于携带)范围可以写为[^[:space:][:print:]] (similar because it doesn't include\ a \ b` -bell and backspace--)。

<file LC_ALL=C   sed   's/[^[:space:][:print:]]//g'  # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^[:space:][:print:]]/,"");print}'
<file            perl   -pe 's/[^[:space:][:print:]]//g;'
<file LC_ALL=C   tr     -dc '[:space:][:print:]'

相关:正则
表达式任何ASCII字符
Perl解决方案
Posix文本文件


请注意,输入到tr可以是任何类型的文件,而不仅仅是文本文件。awk另一方面,需要一个文本文件。
库萨兰达

对于我来说,很难找到其他任何东西来称呼文件“仅ascii字符”而不是“文本文件”(是的,是的:用外行术语来说)。@Kusalananda(无论如何都要添加有关awk的注释)。
NotAnUnixNazi

请注意,这gensub()是gawk扩展。您需要gsub(...); print,并且使用八进制而不是十六进制序列(和LC_ALL = C)是(更多)可移植的。
斯特凡Chazelas

@StéphaneChazelasGNU sed的局限性是什么使得GNU特定于语法(我理解POSIXLY_CORRECT问题)。
NotAnUnixNazi

[^\o0]与POSIX中反斜杠,o和0以外的其他字符匹配sed(在所有实现中,但GNU sed除外)。这不是GNU 的限制sed而是非兼容的扩展,这就是为什么当POSIXLY_CORRECT在环境中时将其禁用的原因。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.