ChatGPT解决这个技术问题 Extra ChatGPT

如何对所有非 ASCII 字符进行 grep?

我有几个非常大的 XML 文件,我试图找到包含非 ASCII 字符的行。我尝试了以下方法:

grep -e "[\x{00FF}-\x{FFFF}]" file.xml

但这会返回文件中的每一行,无论该行是否包含指定范围内的字符。

我的语法错误还是我做错了什么?我也试过:

egrep "[\x{00FF}-\x{FFFF}]" file.xml 

(模式周围有单引号和双引号)。

ASCII 字符只有一个字节长,因此除非文件是 unicode,否则不应有高于 0xFF 的字符。
我们如何超越 \xFF? Grep 给出“grep: range out of order in character class”错误。
有时,对文件中设置了高位的字符有第二个意见是很好的。在这种情况下,我喜欢 tr <file.txt -d '\000-\177' >foo.out && ls -al foo.out 来计数。和/或后跟 od -x foo.out 以查看实际值。
awk solutionC locale + grep 在 BSD 上工作。

K
Kuzeko

您可以使用以下命令:

grep --color='auto' -P -n "[\x80-\xFF]" file.xml

这将为您提供行号,并以红色突出显示非 ascii 字符。

在某些系统中,根据您的设置,上述方法将不起作用,因此您可以通过逆向 grep

grep --color='auto' -P -n "[^\x00-\x7F]" file.xml

另请注意,重要的一点是 -P 标志,它等同于 --perl-regexp:因此它将您的模式解释为 Perl 正则表达式。它还说

这是高度实验性的, grep -P 可能会警告未实现的功能。


这不适用于 BSD grep(在 OS X 10.8 Mountain Lion 上),因为它不支持 P 选项。
为了更新我的最后一条评论,在 Homebrew 的 dupes 库中提供了 GNU 版本的 grep(使用 brew tap homebrew/dupes 启用):brew install grep
@BastiaanVanDeWeerd 是正确的,OSX 10.8 上的 grep 不再支持 PCRE(“Perl 兼容的正则表达式”),因为 Darwin 现在使用 BSD grep 而不是 GNU grep。安装 dupes 库的替代方法是安装 pcrebrew install pcre...作为其中的一部分,您将获得 pcregrep 实用程序,您可以按如下方式使用它:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
对于 Mac brew 用户,可以使用 brew install coreutils 安装 GNU's coreutils。这将为您提供许多以“g”为前缀的 GNU 工具——在本例中使用 ggrep。这应该可以避免因更换系统实用程序而引起的问题,因为系统特定的 Mac 脚本现在依赖于 BSD grep。
这在 mac ag "[\x80-\xFF]" file 上运行良好,您只需要安装 the_silver_searcher
p
pvandenberk

不像上述大多数解决方案那样对非 ASCII 字符的字节范围做出假设,IMO 最好明确说明 ASCII 字符的实际字节范围。

因此,例如,第一个解决方案将变为:

grep --color='auto' -P -n '[^\x00-\x7F]' file.xml

(基本上 greps 用于十六进制 ASCII 范围之外的任何字符:从 \x00 到 \x7F)

在无法运行的 Mountain Lion 上(由于 BSD grep 中缺乏 PCRE 支持),但是通过 Homebrew 安装了 pcre,以下内容也可以正常工作:

pcregrep --color='auto' -n '[^\x00-\x7F]' file.xml

任何人都可以想到的任何优点或缺点?


在上述解决方案失败的情况下,这实际上对我有用。查找 M$ Word 撇号并不容易!
如果你有一个 bash 兼容的 shell 但 pcre-grep 不能工作,LC_COLLATE=C grep $'[^\1-\177]' 工作(对于没有空字节的文件)
此解决方案似乎比上述解决方案更一致。
我不得不使用它在我的 UTF8 文件中拾取汉字、西里尔字母和繁体中文,使用 "[\x80-\xFF]" 错过了所有这些。
专业人士认为这非常有效,而其他选项很好但没有那么好。到目前为止没有发现任何缺点。
T
Thelema

以下对我有用:

grep -P "[\x80-\xFF]" file.xml

非 ASCII 字符从 0x80 开始,在查看字节时转到 0xFF。 Grep(和家族)不进行 Unicode 处理以将多字节字符合并为单个实体以进行正则表达式匹配,如您所愿。我的 grep 中的 -P 选项允许在字符类中使用 \xdd 转义符来完成您想要的。


对于可能无法立即知道如何在多个文件上调用它的视图,只需运行: find 。 -名称 *.xml | xargs grep -P "[\x80-\xFF]"
这确实会返回匹配项,但没有指示字符是什么以及它在哪里。一个人如何看到这个角色是什么,它在哪里?
添加“-n”将给出行号,另外不可见的字符将在终端显示为一个块: grep -n -P "[\x80-\xFF]" file.xml
我在使用 Hangul Korean 时遇到问题:echo '소녀시대' | grep -P "[\x80-\xFF]" 没有为我返回任何信息 -- 其他人可以确认吗? (GNU grep 2.21)
@frabjous 在这里相同,但反向工作:echo '소녀시대' | grep -P "[^\x00-\x7F]"。或者只是使用@slf 指出的the_silver_searcherecho '소녀시대' | ag "[\x80-\xFF]"
G
Gilles 'SO- stop being evil'

简单的方法是将非 ASCII 字符定义为非 ASCII 字符的字符。

LC_ALL=C grep '[^ -~]' file.xml

如有必要,在 ^ 之后添加一个标签。

设置 LC_COLLATE=C 可以避免在许多语言环境中对字符范围的含义产生令人讨厌的意外。设置 LC_CTYPE=C 是匹配单字节字符所必需的—— 否则该命令将错过当前编码中的无效字节序列。设置 LC_ALL=C 完全避免了依赖于语言环境的影响。


在带有 tcsh 的 RedHat 6.4 上,我不得不使用 <<< env LC_COLLATE=C grep -n '[^ -~]' file.xml >>>。我添加了 -n 来获取行号。
对我来说 echo "A" | LC_COLLATE=C grep '[^ -~]' 返回匹配项
@frabjous 如果您有 LC_ALL=en_US.UTF-8,那将胜过 LC_COLLATE 设置。你不应该在你的环境中有这个! LC_ALL 只是强制特定任务使用特定语言环境,通常是 C。要为所有类别设置默认语言环境,请设置 LANG
起初,我没有添加 LC_ALL=C,它在 Mac OS X 和 Ubuntu 上的行为不同。添加此设置后,它们会给出相同的结果。
这适用于 Mac,而其他基于 grep 的解决方案则不能。
n
noquery

在 perl 中

perl -ane '{ if(m/[[:^ascii:]]/) { print  } }' fileName > newFile

在 OSX10.11 上,我必须尝试几种 grep+regex 解决方案,然后才能找到真正有效的解决方案
愿意分享那个 OSX 解决方案@sg?!
上面的 perl 脚本是我正在谈论的解决方案
perl -lne 'print if /[^[:ascii:]]/' file.xml
r
ryanm

这是我发现的另一个变体,它在接受的答案中与 grep 搜索 [\x80-\xFF] 产生了完全不同的结果。也许对某人找到其他非 ascii 字符会很有用:

grep --color='auto' -P -n "[^[:ascii:]]" myfile.txt

注意:我的计算机的 grep(Mac)没有 -P 选项,所以我做了 brew install grep 并使用 ggrep 而不是 grep 开始了上面的调用。


这是迄今为止最好的答案,因为它适用于 Mac 和 Linux。
取决于语言环境。在我将 LC_ALL=C 设置为 LC_ALL=C grep --color='auto' -P -n "[^[:ascii:]]" myfile.txt 之前,它对我不起作用
C
CarenRose

搜索不可打印的字符。 TLDR;执行摘要

搜索控制字符和扩展的 unicode 语言环境设置,例如 LC_ALL=C 需要使 grep 执行您对扩展 unicode 的期望

所以首选的非ASCII字符查找器:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

如最佳答案,逆grep:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

与最佳答案相同,但使用 LC_ALL=C

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test

. .更多的 。 .关于这个的令人难以忍受的细节:。 . .

我同意上面隐藏在评论中的 Harvey,搜索不可打印字符通常更有用,或者当您真的应该考虑不可打印时,很容易想到非 ASCII。 Harvey 建议“使用这个:“[^\n -~]”。为 DOS 文本文件添加 \r。转换为“[^\x0A\x020-\x07E]”并为 CR 添加 \x0D”

此外,在搜索不可打印字符时,将 -c(显示匹配的模式计数)添加到 grep 很有用,因为匹配的字符串可能会弄乱终端。

我发现添加范围 0-8 和 0x0e-0x1f(到 0x80-0xff 范围)是一种有用的模式。这不包括 TAB、CR 和 LF 以及一两个不常见的可打印字符。所以恕我直言,一个非常有用(尽管很粗糙)的 grep 模式是这样的:

grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

实际上,通常您需要这样做:

LC_ALL=C grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

分解:

LC_ALL=C - set locale to C, otherwise many extended chars will not match (even though they look like they are encoded > 0x80)
\x00-\x08 - non-printable control chars 0 - 7 decimal
\x0E-\x1F - more non-printable control chars 14 - 31 decimal
\x80-1xFF - non-printable chars > 128 decimal
-c - print count of matching lines instead of lines
-P - perl style regexps

Instead of -c you may prefer to use -n (and optionally -b) or -l
-n, --line-number
-b, --byte-offset
-l, --files-with-matches

例如使用 find 对当前目录下的所有文件进行 grep 的实际示例:

LC_ALL=C find . -type f -exec grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" {} + 

您可能希望有时调整 grep。例如 BS(0x08 - 退格) 字符用于某些可打印文件或排除 VT(0x0B - 垂直制表符)。在某些情况下,BEL(0x07) 和 ESC(0x1B) 字符也可以被视为可打印的。

Non-Printable ASCII Chars ** 标记 PRINTABLE 但 CONTROL 字符有时有助于排除 Dec Hex Ctrl Char description Dec Hex Ctrl Char description 0 00 ^@ NULL 16 10 ^P DATA LINK ESCAPE (DLE) 1 01 ^A START OF HEADING (SOH) 17 11 ^Q 设备控制 1 (DC1) 2 02 ^B 文本开始 (STX) 18 12 ^R 设备控制 2 (DC2) 3 03 ^C 文本结束 (ETX) 19 13 ^S 设备控制 3 (DC3) 4 04 ^D 传输结束 (EOT) 20 14 ^T 设备控制 4 (DC4) 5 05 ^E 查询结束 (ENQ) 21 15 ^U 否定确认 (NAK) 6 06 ^F 确认 (ACK) 22 16 ^V SYNCHRONIZE (SYN) 7 07 ^G BEEP (BEL) 23 17 ^W 传输块结束 (ETB) 8 08 ^H 退格 (BS)** 24 18 ^X 取消 (CAN) 9 09 ^I 水平制表符 (HT)** 25 19 ^Y 介质结束 (EM) 10 0A ^J 换行符 (LF)** 26 1A ^Z 替换 (SUB) 11 0B ^K 垂直制表符 (VT)** 27 1B ^[ ESCAPE (ESC) 12 0C ^L FF (FORM FEED)** 28 1C ^\ 文件分隔符 (FS) 右箭头 13 0D ^M CR (回车)** 29 1D ^] 组分隔符 (GS) 左箭头 14 0E ^N SO (SHIFT OU T) 30 1E ^^ 记录分隔符 (RS) 向上箭头 15 0F ^O SI (SHIFT IN) 31 1F ^_ 单位分隔符 (US) 向下箭头

更新:我最近不得不重新审视这个。而且,YYMV 取决于终端设置/太阳能天气预报但是。 .我注意到 grep 没有找到很多 unicode 或扩展字符。尽管直觉上它们应该匹配 0x80 到 0xff 的范围,但不匹配 3 和 4 字节的 unicode 字符。 ???谁能解释一下?是的。 @frabjous 问,@calandoa 解释说 LC_ALL=C 应该用于设置命令的语言环境以使 grep 匹配。

例如我的语言环境 LC_ALL= 为空

$ locale
LANG=en_IE.UTF-8
LC_CTYPE="en_IE.UTF-8"
.
.
LC_ALL=

LC_ALL= 为空的 grep 匹配 2 字节编码的字符,但不匹配 3 和 4 字节编码的字符:

$ grep -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" notes_unicode_emoji_test
5:© copyright c2a9
7:call  underscore c2a0
9:CTRL
31:5 © copyright
32:7 call  underscore

带有 LC_ALL=C 的 grep 似乎确实匹配您想要的所有扩展字符:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test  
1:���� unicode dashes e28090
3:��� Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5:� copyright c2a9
7:call� underscore c2a0
11:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29:1 ���� unicode dashes
30:3 ��� Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31:5 � copyright
32:7 call� underscore
33:11 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
34:52 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
81:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other

这个 perl 匹配(部分在 stackoverflow 上的其他地方找到)或顶部答案上的逆 grep 似乎确实找到了所有 ~weird~ 和 ~wonderful~ “non-ascii”字符而不设置语言环境:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test  

1 ‐‐ unicode dashes e28090
3 💘 Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5 © copyright c2a9
7 call  underscore c2a0
9 CTRL-H CHARS URK URK URK 
11 LIVE‐E! あいうえお かが アイウエオ カガ ᚊ ᚋ ซฌ आइ  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29 1 ‐‐ unicode dashes
30 3 💘 Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31 5 © copyright
32 7 call  underscore
33 11 LIVE‐E! あいうえお かが アイウエオ カガ ᚊ ᚋ ซฌ आइ  YEOW, mix of japanese and chars from other
34 52 LIVE‐E! あいうえお かが アイウエオ カガ ᚊ ᚋ ซฌ आइ  YEOW, mix of japanese and chars from other
73 LIVE‐E! あいうえお かが アイウエオ カガ ᚊ ᚋ ซฌ आइ  YEOW, mix of japanese and chars from other

所以首选的非ASCII字符查找器:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

如最佳答案,逆grep:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

与最佳答案相同,但使用 LC_ALL=C

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test

感谢@calandoa 和 frabjous 在上述问题的评论中回答为什么 grep 不匹配以超过 2 个字节编码的字符。在 grep 命令之前使用 LC_ALL=C。
非常感谢您费心发布一个隐藏在 800 个其他支持下的答案!我的问题是 0x02 字符。您可能希望将“实际使用示例”放在顶部附近,因为您真的不需要阅读整篇文章来看看这是否是您的问题。
我知道,真的很老的答案,以及令人痛苦的细节,但我也希望对我和其他人有用。你是对的,我添加了 TLDR;在顶部。
b
bfontaine

以下代码有效:

find /tmp | perl -ne 'print if /[^[:ascii:]]/'

/tmp 替换为您要搜索的目录的名称。


在 Mac 上,这是可行的,而大多数基于 grep 的则不行。
K
Kajukenbo

此方法应适用于任何符合 POSIX 的 awkiconv 版本。我们也可以利用 filetr

当然,curl 不是 POSIX

在某些情况下,上述解决方案可能会更好,但它们似乎依赖于 GNU/Linux 实现或其他工具。

获取示例文件:

$ curl -Ls http://gutenberg.org/files/84/84-0.txt

$ file 84-0.txt

84-0.txt: UTF-8 Unicode (with BOM) text, with CRLF line terminators

搜索 UTF-8 字符:

$ awk '/[\x80-\xFF]/ { print }' 84-0.txt

或非 ASCII

$ awk '/[^[:ascii:]]/ { print }' 84-0.txt

将 UTF-8 转换为 ASCII,删除有问题的字符:

$ iconv -c -t ASCII 84-0.txt > 84-ascii.txt

核实:

$ file 84-ascii.txt

84-ascii.txt: ASCII text, with CRLF line terminators

调整它:

$ tr -d '\015' < 84-ascii.txt | file -

/dev/stdin: ASCII text

YMMV


awk 解决方案适用于 BSD。
d
dma_k

奇怪的是,我今天必须这样做!我最终使用了 Perl,因为我无法让 grep/egrep 工作(即使在 -P 模式下)。就像是:

cat blah | perl -en '/\xCA\xFE\xBA\xBE/ && print "found"'

对于 unicode 字符(例如下面示例中的 \u2212),请使用:

find . ... -exec perl -CA -e '$ARGV = @ARGV[0]; open IN, $ARGV; binmode(IN, ":utf8"); binmode(STDOUT, ":utf8"); while (<IN>) { next unless /\N{U+2212}/; print "$ARGV: $&: $_"; exit }' '{}' \;

在这种情况下,您可能需要检查 stackoverflow.com/a/3208902/7809404 中提到的语言环境
m
miken32

知道如何搜索一个 unicode 字符可能会很有趣。该命令可以提供帮助。你只需要知道UTF8中的代码

grep -v $'\u200d'

我不是真正的专家,但我知道这不是 UTF8 表示,它是 UTF16,或者可能是 UTF32,或 UCS16。对于 2 字节代码点,这三个可能都相同。
n
noabody

查找所有非 ascii 字符给人的印象是,要么正在寻找 unicode 字符串,要么打算单独剥离所述字符。

对于前者,请尝试其中一种(变量 file 用于自动化):

 file=file.txt ; LC_ALL=C grep -Piao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[^\x00-\x19\x21-\x7F]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

如前面的答案所述,如果没有 LC_ALL=C,Vanilla grep 将无法正常工作。

ASCII 范围是 x00-x7F,空格是 x20,因为字符串有空格,负范围会省略它。

非 ASCII 范围是 x80-xFF,因为字符串有空格,正范围添加它。

字符串被假定为该范围内至少 7 个连续字符。 {7,}

对于 shell 可读输出,uchardet $file 返回传递给 iconv 以进行自动插值的文件编码的猜测。


由于提到了 uchardet 命令,这非常有用。感谢您的提醒!
R
RARE Kpop Manifesto

如果您尝试获取/grep UTF8 兼容的多字节字符,请使用以下命令:

(                     [\302-\337][\200-\277]|
                [\340][\240-\277][\200-\277]|
                [\355][\200-\237][\200-\277]|
  [\341-\354\356-\357][\200-\277][\200-\277]|
     [\360][\220-\277][\200-\277][\200-\277]|
[\361-\363][\200-\277][\200-\277][\200-\277]|
     [\364][\200-\217][\200-\277][\200-\277]  ) 

 * please delete all newlines, spaces, or tabs in between (..)

 * feel free to use bracket ranges {1,3} etc to optimize
   the redundant listings of [\200-\277]. but don't change that
   [\200-\277]+, as that might result in invalid encodings 
    due to either insufficient or too many continuation bytes

 * although some historical UTF-8 references considers 5- and 
   6-byte encodings to be valid, as of Unicode 13 they only
   consider up to 4-bytes

我甚至针对随机二进制文件测试了这个字符串,它会报告与 gnu-wc 相同的多字节字符数。

如果您需要完整的 UTF8 匹配字符串,请在前面的 ( 之后添加另一个 [\000-\177]|

这个正则表达式真的很可怕,是的,但它也符合 POSIX、跨语言和跨平台兼容(不依赖于任何特殊的正则表达式符号,(应该)完全符合 UTF-8(Unicode 13),并且完全独立语言环境设置。

如果您正在运行 grep,请使用 grep -P

如果您只需要其他字节,那么其他人已经建议了。

如果您需要 NFC 组成的韩文的 11,172 个字符,那就是

(([\352][\260-\277]|[\353\354][\200-\277]|
 [\355][\200-\235])[\200-\277]|[\355][\236][\200-\243])

如果你需要日语平假名+片假名,那就是

([\343]([\201-\203][\200-\277]|[\207][\260-\277]))