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

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

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

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

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

(在模式周围使用单引号和双引号)。


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

以下是我的工作:

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

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


最简单的方法是定义一个非ascii字符…作为非ASCII字符的字符。

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

如果需要,在^后面添加一个制表符。

设置LC_COLLATE=C可以避免在许多地区中出现关于字符范围含义的令人讨厌的意外。必须设置LC_CTYPE=C以匹配单字节字符-否则该命令将错过当前编码中的无效字节序列。设置LC_ALL=C可以完全避免语言环境相关的影响。


在perl中

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

您可以使用以下命令:

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


不像上面大多数解决方案那样对非ASCII字符的字节范围进行假设,在我看来,明确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

大家能想到什么优点或缺点吗?


这是我发现的另一个变体,它与grep搜索[\x80-\xFF]的结果完全不同。也许它会有用的人找到额外的非ascii字符:

grep -颜色= '汽车' - p - n”[^ [ascii:]]”myfile.txt

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


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

grep -v $'\u200d'

下面的代码工作:

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

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


查找所有非ascii字符会给人留下这样的印象:要么查找unicode字符串,要么打算单独剥离这些字符。

对于前者,可以尝试其中一个(变量文件用于自动化):

 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,因为字符串有空格,所以正范围加上它。

String假定在范围内至少有7个连续字符。{7}。

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


如果你试图抓取/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相同的多字节字符计数。

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

这个正则表达式确实很可怕,但它也符合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]))

这个方法应该适用于任何posix兼容的awk和iconv版本。 我们还可以利用file和tr。

curl当然不是POSIX。

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

只是以某种方式获得一个示例文件:

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

$ file 84-0.txt

84-0.txt: UTF-8 Unicode(带BOM)文本,带有CRLF行终止符

搜索UTF-8字符:

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

或非ascii

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

将UTF-8转换为ASCII,删除有问题的字符(包括BOM,无论如何不应该是UTF-8):

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

检查:

$ file 84-ascii.txt

84-ascii.txt: ASCII文本,带有CRLF行结束符

调整它以删除DOS行结束符/ ^M(“CRLF行终止符”):

$ tr -d '\015' < 84-ascii.txt > 84-tweak .txt &&文件84-tweak .txt

84-tweak .txt: ASCII文本

此方法会丢弃它无法处理的任何“坏”字符,因此您可能需要对输出进行消毒/验证。YMMV