【问题标题】:How do I grep for all non-ASCII characters?如何对所有非 ASCII 字符进行 grep?
【发布时间】:2011-03-01 09:06:51
【问题描述】:

我有几个非常大的 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 以查看实际值。

标签: regex unix unicode grep


【解决方案1】:

此方法适用于任何符合 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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      搜索不可打印的字符。 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
      

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

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

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

      我同意上面隐藏在 cmets 中的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
      ** 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 取决于终端设置/太阳能天气预报但是。 .我注意到 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
      

      grep 与 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~ “非 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
      

      【讨论】:

      • 回答为什么 grep 不匹配编码超过 2 个字节的字符,这要感谢@calandoa 和上面 cmets 中的 frabjous 问题。在 grep 命令之前使用 LC_ALL=C。
      • 非常感谢您费心发布一个隐藏在 800 个其他支持下的答案!我的问题是 0x02 字符。您可能希望将“实际使用示例”放在靠近顶部的位置,因为您真的不需要阅读整篇文章来查看这是否是您的问题。
      • 我知道,答案真的很老,而且细节令人难以忍受,但我也希望对我和其他人有用。你是对的,我添加了 TLDR;在顶部。
      【解决方案4】:

      查找所有非 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 命令。感谢您的提醒!
      【解决方案5】:

      你可以使用命令:

      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 选项。
      • 更新我的最后一条评论,grep 的 GNU 版本在 Homebrew 的 dupes 库中可用(启用使用 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 用户,GNU's coreutils 可以与 brew install coreutils 一起安装。这将为您提供许多以“g”为前缀的 GNU 工具——在本例中使用 ggrep。这应该可以避免因更换系统实用程序而引起的问题,因为系统特定的 Mac 脚本现在依赖于 BSD grep。
      • 这在 Mac 上运行良好 ag "[\x80-\xFF]" file 你只需要安装 the_silver_searcher
      【解决方案6】:

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

      grep -v $'\u200d'
      

      【讨论】:

      • 我不是真正的专家,但我知道这不是 UTF8 表示,它是 UTF16,或者可能是 UTF32,或 UCS16。对于 2 字节代码点,这三个可能都相同。
      【解决方案7】:

      以下代码有效:

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

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

      【讨论】:

      • 在 Mac 上,这是可行的,而大多数基于 grep 的则不行。
      【解决方案8】:

      这是我发现的另一个变体,它在接受的答案中与 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 之前,它对我不起作用
      【解决方案9】:

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

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

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

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

      【讨论】:

      • 在带有 tcsh 的 RedHat 6.4 上,我不得不使用 >>。我添加了 -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 的解决方案则不能。
      【解决方案10】:

      在 perl 中

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

      【讨论】:

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

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

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

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

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

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

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

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

      【讨论】:

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

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

      【讨论】:

      【解决方案13】:

      以下对我有用:

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

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

      【讨论】:

      • 对于可能不立即知道如何在多个文件上调用它的视图,只需运行: find 。 -name *.xml | xargs grep -P "[\x80-\xFF]"
      • 这确实返回了一个匹配,但没有指示字符是什么以及它在哪里。怎么看人物是什么,在哪里?
      • 添加“-n”将给出行号,另外不可见的字符将在终端显示为一个块: grep -n -P "[\x80-\xFF]" 文件。 xml
      • 我在使用韩文时遇到问题:echo '소녀시대' | grep -P "[\x80-\xFF]" 没有为我返回任何信息——其他人可以确认吗? (GNU grep 2.21)
      • @frabjous 此处相同,但反向工作:echo '소녀시대' | grep -P "[^\x00-\x7F]"。或者只是使用@slf 指出的the_silver_searcherecho '소녀시대' | ag "[\x80-\xFF]"
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-04
      • 1970-01-01
      • 1970-01-01
      • 2010-12-04
      • 2011-01-08
      • 2011-03-13
      • 1970-01-01
      相关资源
      最近更新 更多