如何对所有非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库的一种替代方法是安装pcrebrew 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 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  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 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
34 52 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
73 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  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.