perl unicode
耐心看完本文,相信你今后在unicode处理上不会再有什么问题.
本文内容适用于perl 5.8及其以上版本. perl internal form 在Perl看来,字符串只有两种形式. 一种是octets,即8位序列,也就是我们通常说的字节数组. 另一种utf8编码的字符串,perl管它叫string. 也就是说: Perl只认识两种编码: Ascii(octets)和utf8(string). utf8 flag 那么perl如何确定一个字符串是octets还是utf8编码的字符串呢? perl可没有什么智能,他完全是靠字符串上的utf8 flag. 在perl内部,字符串结构由两部分组成: 数据和utf8 flag. 比如字符串"中国"在perl内部的存储是这样: utf8 flag 数据 On 中国 如果utf8 flag是On的话,perl就会把中国当成utf8字符串来处理,如果utf8 flag为Off,perl就会把他当成octets来处理. 所有字符串相关的函数包括正则表达式都会受utf8 flag的影响. 让我们来看个例子: 程序代码: use Encode; use strict; my $str = "中国"; Encode::_utf8_on($str); print length($str) . "n"; Encode::_utf8_off($str); print length($str) . "n"; 运行结果是: 程序代码: 2 6 这里我们使用Encode模块的_utf8_on函数和_utf8_off函数来开关字符串"中国"的utf8 flag. 可以看到,utf8 flag打开的时候,"中国"被当成utf8字符串处理,所以其长度是2. utf8 flag关闭的时候,"中国"被当成octets(字节数组)处理,出来的长度是6(我的编辑器用的是utf8编码,如果你的编辑器用的是gb2312编码,那么长度应该是4). 再来看看正则表达式的例子: 程序代码: use Encode; use strict; my $a = "china----中国"; my $b = "china----中国"; Encode::_utf8_on($a); Encode::_utf8_off($b); $a =~ s/W+//g; $b =~ s/W+//g; print $a,"n"; print $b,"n"; 运行结果: 程序代码: Wide character in print at unicode.pl line 10. china中国 china 结果第一行是一条警告,这个我们稍后再讨论. 结果的第二行说明,utf8 flag开启的情况下,正则表达式中的w能够匹配中文,反之则不能. 如何确定一个字符串的utf8 flag是否已开启? 使用Encode::is_utf8($str). 这个函数并不是用来检测一个字符串是不是utf8编码,而是仅仅看看它的utf8 flag是否开启. eq eq是一个字符串比较操作符,只有当字符串的内容一致并且utf8 flag的状态也是一致的时候,eq才会返回真. 理论就是上面这些,一定要搞明白,记清楚! 下面是实际应用. unicode转码 如果你有一个字符串"中国",它是gb2312编码的. 如果它的utf8 flag是关闭的,它就会被当成octets来处理,length()会返回4,这通常不是你想要的. 而如果你开启它的utf8 flag,则它会被当做utf8编码的字符串来处理. 由于它本来的编码是gb2312的,不是utf8的,这就可能导致错误发生. 由于gb2312和utf8内码范围部分重叠,所以很多时候,不会有错误报出来,但是perl可能已经错误地拆解了字符. 严重的时候,perl会报警,说某个字节不是合法的utf8内码. 解决的方法很显然,如果你的字符串本来不是utf8编码的,应该先把它转成utf8编码,并且使它的utf8 flag处于开启状态. 对于一个gb2312编码的字符串,你可以使用 程序代码: $str = Encode::decode("gb2312",$str); 来将其转化为utf8编码并开启utf8 flag. 如果你的字符串编码本来就是utf8,只是utf8 flag没有打开,那么你可以使用以下三种方式中的任一种来开启utf8 flag: 程序代码: $str = Encode::decode_utf8($str); $str = Encode::decode("utf8",$str); Encode::_utf8_on($str); 最后一种方式效率最高,但是官方不推荐. 以下划线开头的函数是内部函数,出于礼貌,一般不从外部调用. 字符串连接 . 是字符串连接操作符. 连接两个字符串时,如果两个字符串的utf8 flag都是Off,那么结果字符串也是Off. 如果其中任何一个字符串的utf8 flag是On的话,那么结果字符串的utf8 flag将是On. 连接字符串并不会改变它们原来的编码,所以如果你把两个不同编码的字符串连在一起,那么以后不管对这个字符串怎么转码,都总会有一段是乱码. 这种情况一定要避免,连接两个字符串之前应该确保它们编码一致. 如有必要,先进行转码,再连接字符串. perl unicode编程基本原则 对于任何要处理的unicode字符串,1)把它的编码转换成utf8; 2)开启它的utf8 flag 字符串来源 为了应用上面说到的基本原则,我们首先要知道字符串本来的编码和utf8 flag开关情况,这里我们讨论几种情况. 1) 命令行参数和标准输入. 从命令行参数或标准输入(STDIN)来的字符串,它的编码跟locale有关. 如果你的locale是zh_CN或zh_CN.gb2312,那么进来的字符串就是gb2312编码,如果你的locale是zh_CN.gbk,那么进来的编码就是gbk,如果你的编码是zh_CN.UTF8,那进来的编码就是utf8. 不管是什么编码,进来的字符串的utf8 flag都是关闭的状态. 2) 你的源代码里的字符串. 这要看你编写源代码时用的是什么编码. 在editplus里,你可以通过"文件"->"另存为"查看和更改编码. 在linux下,你可以cat一个源代码文件,如果中文正常显示,说明源代码的编码跟locale是一致的. 源代码里的字符串的utf8 flag同样是关闭的状态. 如果你的源代码里含有中文,那么你最好遵循这个原则: 1) 编写代码时使用utf8编码,2)在文件的开头加上use utf8;语句. 这样,你源代码里的字符串就都会是utf8编码的,并且utf8 flag也已经打开. 3) 从文件读入. 这个毫无疑问,你的文件是什么编码,读进来就是什么编码了. 读进来以后,utf8 flag是off状态. 4) 抓取网页. 网页是什么编码就是什么编码,utf8 flag是off状态. 网站的编码可以从响应头里或者html的<head>标签里获得. 也有可能出现响应头和html head里都没说明编码的情况,这个就是做的很不礼貌的网页了. 这时候只能用程序来猜: 程序代码: use Encode; use LWP::Simple qw(get); use strict; my $str = get "http://www.sina.com.cn"; eval {my $str2 = $str; Encode::decode("gbk",$str2,1)}; print "not gbk: $@n" if $@; eval {my $str2 = $str; Encode::decode("utf8",1)}; print "not utf8: $@n" if $@; eval {my $str2 = $str; Encode::decode("big5",1)}; print "not big5: $@n" if $@; 输出: 程序代码: not utf8: utf8 "xD0" does not map to Unicode at /usr/local/lib/perl/5.8.8/ Encode.pm line 162. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |