如何grep所有非ASCII字符?

时间:2010-06-08 20:48:14

标签: regex unix unicode grep

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

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

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

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

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

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

12 个答案:

答案 0 :(得分:439)

您可以使用命令:

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可能会警告未实现   特征

答案 1 :(得分:108)

与上述大多数解决方案一样,不是对非ASCII字符的字节范围进行假设,而是明确更好地了解ASCII字符的实际字节范围。

所以第一个解决方案就是:

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

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

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

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

任何人都可以想到的任何利弊?

答案 2 :(得分:66)

以下适用于我:

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

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

答案 3 :(得分:47)

在perl

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

答案 4 :(得分:38)

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

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

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

设置LC_COLLATE=C避免了许多语言环境中字符范围含义的令人讨厌的意外。设置LC_CTYPE=C是匹配单字节字符所必需的 - 否则命令将错过当前编码中的无效字节序列。设置LC_ALL=C完全避免了与语言环境相关的影响。

答案 5 :(得分:21)

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

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

注意:我的计算机的grep(Mac)没有-P选项,因此我执行了brew install grep并使用ggrep而不是grep开始了上述调用。< / p>

答案 6 :(得分:8)

以下代码有效:

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

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

答案 7 :(得分:4)

搜索不可打印的字符。

我同意上面隐藏在评论中的Harvey,搜索不可打印的字符通常更有用,或者当你真的应该考虑不可打印时很容易想到非ASCII。 Harvey建议“使用这个:”[^ \ n - 〜]“。为DOS文本文件添加\ r \ n。转换为”[^ \ 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]" *

击穿:

\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

E.g。使用find的实际例子grep当前目录下的所有文件:

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

答案 8 :(得分: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 }' '{}' \;

答案 9 :(得分:0)

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

grep -v $'\u200d'

答案 10 :(得分:0)

查找所有非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以进行自动插值。

答案 11 :(得分:0)

如果您尝试抓取/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

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

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

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

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

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