使用sed,正则表达式如何匹配汉字?

时间:2014-04-20 22:14:23

标签: regex bash utf-8 sed chinese-locale

我决定发布一个问题,花了很长时间后仍然没有弄清问题。还阅读了一堆看似相关的帖子,没有一个真的适合我的简单(?)问题。

所以我有一个可能很大的文本文件(> 1000行),其中包含普通话中文字符,其示例行如下:

"ref#2-5-1.jpg#2#一些 <variable> 内容#pic##" (the Chinese just means "some content"). 

所有需要修改的是,如果没有一个空格,应该在每个角色之间插入一个空格:

"ref#2-5-1.jpg#2#一 些 <variable> 内 容#pic##".

我开始天真地用以下简单的东西,但根本没有匹配:

sed -e 's/\([\u4E00-\u9fff]\)/\1 /g' <test_utf_sed.txt > test_out.txt

其中4E00-9fff应该是普通话的代码范围。 毫无疑问,这没有用,所以我也想尝试

sed -e 's/\([一-龻]\)/hello/g' <test_utf_sed.txt > test_out.txt

这失败了,因为我的bash无法显示(?)“一”字符。

然后我做了一些基本测试,但也失败了:

sed -e 's/\(\u4E00\)/hello/g' <test_utf_sed.txt > test_out.txt //一
sed -e 's/\(\u4E9B\)/hello/g' <test_utf_sed.txt > test_out.txt //些

与utf编码的另一种表示法相同(在stackoverflow上找到):

sed -e 's/\(\u'U+4E00\)/hello/g' <test_utf_sed.txt > test_out.txt

1)作为处理双字节字符的工具,可以选择正确的选择吗?

2)是否能够完全处理unicode,还是需要一个特殊的开关?

3)我正在寻找这样的解决方案:

step1: insert space after each character 
  //like 's/\(.\)/\1 /g')
step2: remove space after each chacter which is not a Chinese character 
  //like 's/\([a-zA-Z0-9]\) /\1/g')

我知道如何做到这一点,但它不够优雅且容易出错。这必须可以在sed中使用正则表达式中的utf-8。

4)我的环境在 MacOS 10.6.8 (旧式操作系统)上 bash-3.2

5)如果您知道某些开放的regEx-onliners指向中文文本或语言处理的库,那么分享会很棒。

非常感谢您,非常感谢您的帮助!

2 个答案:

答案 0 :(得分:5)

Perl对处理Unicode有很好的支持。对于你的任务而言,这可能比sed更好。这个单行程就像你的第一个sed示例一样:

perl -CIOED -p -e 's/\p{Block=CJK_Unified_Ideographs}/$& /g' filename

-CIOED告诉perl在utf8中执行其I / O. -p为输入文件的每一行运行给定代码一次,然后打印结果。 -e指定要运行的一行Perl代码。有关更多信息,请参阅命令行参数的the documentation

正则表达式使用named ranges来标识要匹配的字符。

您可能还想阅读Perl Unicode文档。

答案 1 :(得分:2)

sed并不了解\u转义序列(显然)。我不知道bash-3.2是否也有,但我认为确实如此;如果是这样,你可以写

sed $'s/\u4E9B/hello/g'

但你仍然无法进行范围规范。

但是,通过手动翻译为UTF-8,您可以得到以下扩展正则表达式,我相信,它将匹配U +范围内某个字符的任何UTF-8序列4E00 ... U + 9FFF:

(\xe4[\xb8-\xbf][\x80-\xbf]|[\xe5-\xe9][\x80-\xbf][\x80-\xbf])

(但是只有在单字节语言环境中调用sed时,字符范围才有效,最好是C语言环境。)

使用GNU sed,如果提供-r标志,则会获得扩展的正则表达式。使用MacOSX,我相信你需要-E标志。所以你可以试试:

LANG=C sed -E \
       $'s/(\xe4[\xb8-\xbf][\x80-\xbf]|[\xe5-\xe9][\x80-\xbf][\x80-\xbf])/\\1 /g' \
       <test_utf_sed.txt >test_out.txt

(上面让bash处理\x转义。如果你忽略了$,那么sed将会处理\x转义,但是你会必须将替换从\\1更改为\1。我没有Mac,也没有旧版本的bash,所以我真的不知道你的{{{ 1}}十六进制是否逃脱;我非常确定你的bash会,但我无法保证。)


顺便说一句,对这些角色进行utf-8编码并不困难;我用原始帖子中的一些复制粘贴做了。例如:

sed

有助于知道平面0表意文字(U + 4E00 ... U + 9FFF)的整个范围都有三字节代码,因此一个是$ hd <<<"一些" 00000000 e4 b8 80 e4 ba 9b 0a |.......| 而一些是E4 B8 80。 (E4 BA 9B当然是一个行尾。)