我有一个文件,a.out,其中包含许多行。每行只有一个字符,可以是unicode字符U+2013
,也可以是小写字母a-z
。
在a.out上执行文件命令会引出结果UTF-8 Unicode文本。
locale命令报告:
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
如果我发出命令grep -P -n "[^\x00-\xFF]" a.out
,我希望只返回包含U+2013
的行。如果我在cygwin下进行测试就是这种情况。然而,问题环境是Oracle Linux Server 6.5版,问题是grep命令不返回任何行。如果我发出grep -P -n "[\x00-\xFF]
" a.out然后返回所有行。
我意识到" [grep -P]
...是高度实验性的,grep -P
可能会警告未实现的功能。"但没有发出警告。
我错过了什么吗?
答案 0 :(得分:3)
我建议避免使用狡猾的grep -P
实现并使用真实的东西。这有效:
perl -CSD -nle 'print "$.: $_" if /\P{ASCII}/' utfile1 utfile2 utfile3 ...
其中:
-CSD
选项表示stdio trio(stdin,stdout,stderr)和磁盘文件都应视为UTF-8编码。
$.
代表当前记录(行)编号。
$_
代表当前行。
\P{ASCII}
匹配非 ASCII的任何代码点。
答案 1 :(得分:0)
How Do I grep For all non-ASCII Characters in UNIX中的评论给出了答案:
Grep(和系列)不进行Unicode处理,将多字节字符合并为单个实体,以便进行正则表达式匹配。
这意味着U+2013
(0xe2
,0x80
,0x93
)的UTF-8编码不被grep视为单个可打印字符的一部分给定范围。
GNU grep手册的d escription of -P
未提及Unicode或UTF-8。相反,它说将模式解释为Perl正则表达式。(这并不意味着结果与Perl的相同,只是某些反斜杠转义< EM>类似)。
Perl本身可以告知使用UTF-8编码。但是,Filtering invalid utf8中使用Perl的示例不使用该功能。相反,表达式(如问题grep中的表达式)仅测试单个字节 - 而不是完整字符。
答案 2 :(得分:0)
gawk可以帮助您解决这个问题,
这是awk one-liner:
awk -v FS="" 'BEGIN{for(i=1;i<128;i++)ord[sprintf("%c",i)]=i}
{for(i=1;i<=NF;i++)if(!($i in ord))print $i}' file
下面是gawk的测试:
kent$ cat f
abcd
+ß
s+äö
ö--我
中文
kent$ awk -v FS="" 'BEGIN{for(i=1;i<128;i++)ord[sprintf("%c",i)]=i}{for(i=1;i<=NF;i++)if(!($i in ord))print $i}' f
ß
ä
ö
ö
我
中
文