我有几个非常大的 XML 文件,我试图找到包含非 ASCII 字符的行。我尝试了以下方法:
grep -e "[\x{00FF}-\x{FFFF}]" file.xml
但这会返回文件中的每一行,无论该行是否包含指定范围内的字符。
我的语法错误还是我做错了什么?我也试过:
egrep "[\x{00FF}-\x{FFFF}]" file.xml
(模式周围有单引号和双引号)。
tr <file.txt -d '\000-\177' >foo.out && ls -al foo.out
来计数。和/或后跟 od -x foo.out
以查看实际值。
您可以使用以下命令:
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 字符的字节范围做出假设,IMO 最好明确说明 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
任何人都可以想到的任何优点或缺点?
LC_COLLATE=C grep $'[^\1-\177]'
工作(对于没有空字节的文件)
以下对我有用:
grep -P "[\x80-\xFF]" file.xml
非 ASCII 字符从 0x80 开始,在查看字节时转到 0xFF。 Grep(和家族)不进行 Unicode 处理以将多字节字符合并为单个实体以进行正则表达式匹配,如您所愿。我的 grep 中的 -P
选项允许在字符类中使用 \xdd
转义符来完成您想要的。
echo '소녀시대' | grep -P "[\x80-\xFF]"
没有为我返回任何信息 -- 其他人可以确认吗? (GNU grep 2.21)
echo '소녀시대' | grep -P "[^\x00-\x7F]"
。或者只是使用@slf 指出的the_silver_searcher
:echo '소녀시대' | ag "[\x80-\xFF]"
简单的方法是将非 ASCII 字符定义为非 ASCII 字符的字符。
LC_ALL=C grep '[^ -~]' file.xml
如有必要,在 ^
之后添加一个标签。
设置 LC_COLLATE=C
可以避免在许多语言环境中对字符范围的含义产生令人讨厌的意外。设置 LC_CTYPE=C
是匹配单字节字符所必需的—— 否则该命令将错过当前编码中的无效字节序列。设置 LC_ALL=C
完全避免了依赖于语言环境的影响。
echo "A" | LC_COLLATE=C grep '[^ -~]'
返回匹配项
LC_ALL=en_US.UTF-8
,那将胜过 LC_COLLATE
设置。你不应该在你的环境中有这个! LC_ALL
只是强制特定任务使用特定语言环境,通常是 C
。要为所有类别设置默认语言环境,请设置 LANG
。
LC_ALL=C
,它在 Mac OS X 和 Ubuntu 上的行为不同。添加此设置后,它们会给出相同的结果。
在 perl 中
perl -ane '{ if(m/[[:^ascii:]]/) { print } }' fileName > newFile
perl -lne 'print if /[^[:ascii:]]/' file.xml
这是我发现的另一个变体,它在接受的答案中与 grep 搜索 [\x80-\xFF]
产生了完全不同的结果。也许对某人找到其他非 ascii 字符会很有用:
grep --color='auto' -P -n "[^[:ascii:]]" myfile.txt
注意:我的计算机的 grep(Mac)没有 -P
选项,所以我做了 brew install grep
并使用 ggrep
而不是 grep
开始了上面的调用。
LC_ALL=C
设置为 LC_ALL=C grep --color='auto' -P -n "[^[:ascii:]]" myfile.txt
之前,它对我不起作用
搜索不可打印的字符。 TLDR;执行摘要
搜索控制字符和扩展的 unicode 语言环境设置,例如 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
. .更多的 。 .关于这个的令人难以忍受的细节:。 . .
我同意上面隐藏在评论中的 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 ** 标记 PRINTABLE 但 CONTROL 字符有时有助于排除 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 设备控制 1 (DC1) 2 02 ^B 文本开始 (STX) 18 12 ^R 设备控制 2 (DC2) 3 03 ^C 文本结束 (ETX) 19 13 ^S 设备控制 3 (DC3) 4 04 ^D 传输结束 (EOT) 20 14 ^T 设备控制 4 (DC4) 5 05 ^E 查询结束 (ENQ) 21 15 ^U 否定确认 (NAK) 6 06 ^F 确认 (ACK) 22 16 ^V SYNCHRONIZE (SYN) 7 07 ^G BEEP (BEL) 23 17 ^W 传输块结束 (ETB) 8 08 ^H 退格 (BS)** 24 18 ^X 取消 (CAN) 9 09 ^I 水平制表符 (HT)** 25 19 ^Y 介质结束 (EM) 10 0A ^J 换行符 (LF)** 26 1A ^Z 替换 (SUB) 11 0B ^K 垂直制表符 (VT)** 27 1B ^[ ESCAPE (ESC) 12 0C ^L FF (FORM FEED)** 28 1C ^\ 文件分隔符 (FS) 右箭头 13 0D ^M CR (回车)** 29 1D ^] 组分隔符 (GS) 左箭头 14 0E ^N SO (SHIFT OU T) 30 1E ^^ 记录分隔符 (RS) 向上箭头 15 0F ^O SI (SHIFT IN) 31 1F ^_ 单位分隔符 (US) 向下箭头
更新:我最近不得不重新审视这个。而且,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
带有 LC_ALL=C
的 grep 似乎确实匹配您想要的所有扩展字符:
$ 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
与最佳答案相同,但使用 LC_ALL=C
:
$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test
以下代码有效:
find /tmp | perl -ne 'print if /[^[:ascii:]]/'
将 /tmp
替换为您要搜索的目录的名称。
此方法应适用于任何符合 POSIX 的 awk
和 iconv
版本。我们也可以利用 file
和 tr
。
在某些情况下,上述解决方案可能会更好,但它们似乎依赖于 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
奇怪的是,我今天必须这样做!我最终使用了 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 }' '{}' \;
知道如何搜索一个 unicode 字符可能会很有趣。该命令可以提供帮助。你只需要知道UTF8中的代码
grep -v $'\u200d'
查找所有非 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
命令,这非常有用。感谢您的提醒!
如果您尝试获取/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]))
grep
(在 OS X 10.8 Mountain Lion 上),因为它不支持P
选项。dupes
库中提供了 GNU 版本的grep
(使用brew tap homebrew/dupes
启用):brew install grep
dupes
库的替代方法是安装pcre
:brew install pcre
...作为其中的一部分,您将获得pcregrep
实用程序,您可以按如下方式使用它:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
brew
用户,可以使用brew install coreutils
安装 GNU's coreutils。这将为您提供许多以“g”为前缀的 GNU 工具——在本例中使用ggrep
。这应该可以避免因更换系统实用程序而引起的问题,因为系统特定的 Mac 脚本现在依赖于 BSD grep。ag "[\x80-\xFF]" file
上运行良好,您只需要安装the_silver_searcher