如何对所有非A​​SCII字符进行grep?


359

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

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

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

我的语法有误还是做错了其他事情?我也尝试过:

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

(用单引号和双引号将模式引起来)。


ASCII字符只有一个字节长,因此,除非文件是unicode,否则0xFF以上的字符都不应存在。
— zdav

我们如何超越\ xFF?Grep给出“ grep:字符类中的范围乱序”错误。
— Mudit Jain 2014年

Answers:


493

您可以使用以下命令:

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可能会警告未实现的功能。


42
这在BSD grep(在OS X 10.8 Mountain Lion上)不起作用,因为它不支持该P选项。
— 巴斯蒂安·范·德·韦德

20
要更新我的最新评论,grepHomebrew的dupes库中提供了GNU版本(启用可以使用brew tap homebrew/dupes):brew install grep
— Bastiaan M. van de Weerd 2012年

48
@BastiaanVanDeWeerd是正确的,OSX 10.8上的grep不再支持PCRE(“与Perl兼容的正则表达式”),因为达尔文现在使用BSD grep而不是GNU grep。安装dupes库的一种替代方法是安装pcre:brew install pcre...作为此过程的一部分,您将获得该pcregrep实用程序,您可以按以下方式使用该实用程序:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
— pvandenberk 2012年

15
对于Mac brew用户,可以使用安装GNU的coreutilsbrew install coreutils。这将为您提供许多以'g'开头的GNU工具-在这种情况下,请使用ggrep。这应该避免替换系统实用程序引起的问题,因为特定于系统的Mac脚本现在依赖于BSD grep。
— 2014年

22
这在ag "[\x80-\xFF]" file您只需要安装的Mac上运行良好the_silver_searcher
— slf

123

像上面的大多数解决方案一样,与其对非ASCII字符的字节范围进行假设,还不如对ASCII字符的实际字节范围进行明确表示,IMO更好。

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

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

(基本上,对于十六进制ASCII范围之外的任何字符,它都会陷入混乱:从\ x00到\ x7F)

在不起作用的Mountain Lion上(由于BSD grep中缺少PCRE支持),但pcre通过Homebrew 进行安装,以下内容同样适用:

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

任何人都可以考虑的利弊?


9
在上述解决方案失败的情况下,这实际上对我有用。查找M $ Word撇号并非易事!
— AlbertEngelB 2015年

2
如果您具有与bash兼容的外壳,但不能使用pcre-grep,则LC_COLLATE=C grep $'[^\1-\177]'可以使用(适用于无空字节的文件)
— idupree 2015年

2
该解决方案似乎比上述解决方案更加一致。
— 2015年

1
我不得不使用它来在我的UTF8文件中提取汉字,西里尔字母和繁体中文,而使用“ [\ x80- \ xFF]”却错过了所有这些。
— buckaroo1177125

1
专家认为,这是出色的,而其他选项都不错,但效果却不那么好。到目前为止,没有发现任何弊端。
— jwpfox

67

以下对我有用:

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

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


1
对于可能不立即知道如何在多个文件中调用此视图的视图,只需运行:find。名称* .xml | xargs grep -P“ [\ x80- \ xFF]”
— David Mohundro 2010年

1
这确实会返回一个匹配项,但没有指示该字符是什么以及它在哪里。如何看待角色是什么以及角色在哪里?
— Faheem Mitha

添加“ -n”将给出行号,另外,不可见的字符将在终端上显示为块:grep -n -P“ [\ x80- \ xFF]” file.xml
— fooMonster 2011年

4
我的韩文韩文有问题:echo '소녀시대' | grep -P "[\x80-\xFF]"对我没有任何回报-其他人可以确认吗?(GNU grep 2.21)
— frabjous 2015年

@frabjous这里也一样,但是grepping相反的工作:echo '소녀시대' | grep -P "[^\x00-\x7F]"。或只使用the_silver_searcher@slf指出的方式:echo '소녀시대' | ag "[\x80-\xFF]"
— psmith

55

在Perl中

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

1
在OSX10.11上,我不得不尝试几种grep + regex解决方案,然后才能找到切实可行的方法
— sg

想要分享该OSX解决方案@sg ?!
— geotheory,2015年

上面的perl脚本是我正在谈论的解决方案
— sg

5
perl -lne 'print if /[^[:ascii:]]/' file.xml
— 纪念馆

43

最简单的方法是将非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以获取行号。
— ddevienne 2014年

对我来说echo "A" | LC_COLLATE=C grep '[^ -~]'是一场比赛
— 2015年

1
@frabjous如果您有LC_ALL=en_US.UTF-8,那将胜过LC_COLLATE设置。您不应该在您的环境中使用它!LC_ALL通常只是强制特定任务使用特定语言环境C。要为所有类别设置默认语言环境,请设置LANG。
— 吉勒斯(Gilles)'所以

1
最初,我没有添加LC_ALL=C,它在Mac OS X和Ubuntu上的行为有所不同。添加此设置后,它们将得到相同的结果。
— Max Peng

1
在Mac上可以使用,而其他基于grep的解决方案则不能。
— 马提亚斯·弗里普

26

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

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

注意:我的计算机的grep(苹果机)没有此-P选项,因此我选择了,并brew install grep使用ggrep代替进行了上述调用grep。


2
到目前为止,这是最好的答案,因为它适用于Mac和Linux。
— tommy.carstensen 18-3-12

在Linux上只有一个对我有用。

9

以下代码有效:

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

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


2
在Mac上,此方法有效,而大多数基于grep的软件则无效。
— 马提亚斯·弗里普

9

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

  1. 搜索控制字符和扩展的unicode
  2. 区域设置,例如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

如最佳答案,但WITH 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“

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

我发现将范围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 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
** marks PRINTABLE but CONTROL chars that is useful to exclude sometimes
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  DEVICE CONTROL 1 (DC1)
2     02  ^B  START OF TEXT (STX)         18  12  ^R  DEVICE CONTROL 2 (DC2)
3     03  ^C  END OF TEXT (ETX)           19  13  ^S  DEVICE CONTROL 3 (DC3)
4     04  ^D  END OF TRANSMISSION (EOT)   20  14  ^T  DEVICE CONTROL 4 (DC4)
5     05  ^E  END OF QUERY (ENQ)          21  15  ^U  NEGATIVE ACKNOWLEDGEMENT (NAK)
6     06  ^F  ACKNOWLEDGE (ACK)           22  16  ^V  SYNCHRONIZE (SYN)
7     07  ^G  BEEP (BEL)                  23  17  ^W  END OF TRANSMISSION BLOCK (ETB)
8     08  ^H  BACKSPACE (BS)**            24  18  ^X  CANCEL (CAN)
9     09  ^I  HORIZONTAL TAB (HT)**       25  19  ^Y  END OF MEDIUM (EM)
10    0A  ^J  LINE FEED (LF)**            26  1A  ^Z  SUBSTITUTE (SUB)
11    0B  ^K  VERTICAL TAB (VT)**         27  1B  ^[  ESCAPE (ESC)
12    0C  ^L  FF (FORM FEED)**            28  1C  ^\  FILE SEPARATOR (FS) RIGHT ARROW
13    0D  ^M  CR (CARRIAGE RETURN)**      29  1D  ^]  GROUP SEPARATOR (GS) LEFT ARROW
14    0E  ^N  SO (SHIFT OUT)              30  1E  ^^  RECORD SEPARATOR (RS) UP ARROW
15    0F  ^O  SI (SHIFT IN)               31  1F  ^_  UNIT SEPARATOR (US) DOWN ARROW

更新:我最近不得不重新审视它。并且,YYMV取决于终端设置/太阳能天气预报BUT。。我注意到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字节编码的char,但不匹配3和4字节编码的char:

$ 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

grep with LC_ALL=C确实与您想要的所有扩展字符匹配:

$ 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

如最佳答案,但WITH LC_ALL=C:

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

1
回答为什么grep不能匹配编码超过2个字节的字符,这要归功于上述问题中@calandoa和frabjous。在grep命令之前使用LC_ALL = C。
— gaoithe

1
非常感谢您愿意在其他800项投票中发表答案!我的问题是0x02字符。您可能希望将“实用的使用示例”放在顶部,因为您真的不需要阅读整篇文章,只需看看这是否是您的问题。
— Noumenon

1
我知道,答案确实很旧,并且包含很多细节,但我希望对我和其他人有用。没错,我添加了TLDR;在顶部。
— gaoithe

1

奇怪的是,我今天必须这样做!我最终使用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 }' '{}' \;

1

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

grep -v $'\u200d'

我并不是真正的专家,但是我知道这不是UTF8表示形式,而是UTF16或UTF32或UCS16。对于2字节的代码点,这三个可能全部相同。
— Baxissimo

1

查找所有非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,香草grep将无法正常工作。

ASCII范围为x00-x7F,空格为x20,因为字符串的空格为负范围,则忽略它。

非ASCII范围是x80-xFF,因为字符串之间有空格,所以正范围会将其相加。

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

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


由于提到了uchardet命令,这非常有用。感谢您的提示!
— bballdave025
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.