Perl Unicode全程攻略

开发 架构
Perl Unicode的概念你是否了解,本文向大家简单介绍一下Perl Unicode全攻略,希望本文的介绍能让你有所收获。

本文和大家重点学习一下Perl Unicode全攻略,耐心看完本文,相信你今后在Perl Unicode处理上不会再有什么问题,希望你对本文介绍会感兴趣。

Perl Unicode全攻略

本文内容适用于perl5.8及其以上版本.

perlinternalform

在Perl看来,字符串只有两种形式.一种是octets,即8位序列,也就是我们通常说的字节数组.另一种utf8编码的字符串,perl管它叫string.也就是说:Perl只认识两种编码:Ascii(octets)和utf8(string).

utf8flag

那么perl如何确定一个字符串是octets还是utf8编码的字符串呢?perl可没有什么智能,他完全是靠字符串上的utf8flag.在perl内部,字符串结构由两部分组成:数据和utf8flag.比如字符串"中国"在perl内部的存储是这样:

utf8flag数据
On中国

如果utf8flag是On的话,perl就会把中国当成utf8字符串来处理,如果utf8flag为Off,perl就会把他当成octets来处理.所有字符串相关的函数包括正则表达式都会受utf8flag的影响.让我们来看个例子:

程序代码:
 

  1. useEncode;  
  2. usestrict;  
  3.  
  4. my$str="中国";  
  5. Encode::_utf8_on($str);  
  6. printlength($str)."\n";  
  7. Encode::_utf8_off($str);  
  8. printlength($str)."\n";  

 运行结果是:

程序代码:
2
6

这里我们使用Encode模块的_utf8_on函数和_utf8_off函数来开关字符串"中国"的utf8flag.可以看到,utf8flag打开的时候,"中国"被当成utf8字符串处理,所以其长度是2.utf8flag关闭的时候,"中国"被当成octets(字节数组)处理,出来的长度是6(我的编辑器用的是utf8编码,如果你的编辑器用的是gb2312编码,那么长度应该是4).

再来看看正则表达式的例子:

程序代码:
 

  1. useEncode;  
  2. usestrict;  
  3.  
  4. my$a="china----中国";  
  5. my$b="china----中国";  
  6. Encode::_utf8_on($a);  
  7. Encode::_utf8_off($b);  
  8. $a=~s/\W+//g;  
  9. $b=~s/\W+//g;  
  10. print$a,"\n";  
  11. print$b,"\n";  

 运行结果:

程序代码:
WidecharacterinprintatPerl Unicode.plline10.
china中国
china

结果***行是一条警告,这个我们稍后再讨论.结果的第二行说明,utf8flag开启的情况下,正则表达式中的\w能够匹配中文,反之则不能.

如何确定一个字符串的utf8flag是否已开启?使用Encode::is_utf8($str).这个函数并不是用来检测一个字符串是不是utf8编码,而是仅仅看看它的utf8flag是否开启.

eq

eq是一个字符串比较操作符,只有当字符串的内容一致并且utf8flag的状态也是一致的时候,eq才会返回真.

理论就是上面这些,一定要搞明白,记清楚!下面是实际应用.#p#

Perl Unicode转码

如果你有一个字符串"中国",它是gb2312编码的.如果它的utf8flag是关闭的,它就会被当成octets来处理,length()会返回4,这通常不是你想要的.而如果你开启它的utf8flag,则它会被当做utf8编码的字符串来处理.由于它本来的编码是gb2312的,不是utf8的,这就可能导致错误发生.由于gb2312和utf8内码范围部分重叠,所以很多时候,不会有错误报出来,但是perl可能已经错误地拆解了字符.严重的时候,perl会报警,说某个字节不是合法的utf8内码.

解决的方法很显然,如果你的字符串本来不是utf8编码的,应该先把它转成utf8编码,并且使它的utf8flag处于开启状态.对于一个gb2312编码的字符串,你可以使用

程序代码:
 

  1. $str=Encode::decode("gb2312",$str); 

来将其转化为utf8编码并开启utf8flag.如果你的字符串编码本来就是utf8,只是utf8flag没有打开,那么你可以使用以下三种方式中的任一种来开启utf8flag:

程序代码:

  1. $str=Encode::decode_utf8($str);  
  2. $str=Encode::decode("utf8",$str);  
  3. Encode::_utf8_on($str); 

***一种方式效率***,但是官方不推荐.以下划线开头的函数是内部函数,出于礼貌,一般不从外部调用.

字符串连接

.是字符串连接操作符.连接两个字符串时,如果两个字符串的utf8flag都是Off,那么结果字符串也是Off.如果其中任何一个字符串的utf8flag是On的话,那么结果字符串的utf8flag将是On.连接字符串并不会改变它们原来的编码,所以如果你把两个不同编码的字符串连在一起,那么以后不管对这个字符串怎么转码,都总会有一段是乱码.这种情况一定要避免,连接两个字符串之前应该确保它们编码一致.如有必要,先进行转码,再连接字符串.

Perl Unicode编程基本原则

对于任何要处理的Perl Unicode字符串,1)把它的编码转换成utf8;2)开启它的utf8flag

字符串来源

为了应用上面说到的基本原则,我们首先要知道字符串本来的编码和utf8flag开关情况,这里我们讨论几种情况.

1)命令行参数和标准输入.从命令行参数或标准输入(STDIN)来的字符串,它的编码跟locale有关.如果你的locale是zh_CN或zh_CN.gb2312,那么进来的字符串就是gb2312编码,如果你的locale是zh_CN.gbk,那么进来的编码就是gbk,如果你的编码是zh_CN.UTF8,那进来的编码就是utf8.不管是什么编码,进来的字符串的utf8flag都是关闭的状态.

2)你的源代码里的字符串.这要看你编写源代码时用的是什么编码.在editplus里,你可以通过"文件"->"另存为"查看和更改编码.在linux下,你可以cat一个源代码文件,如果中文正常显示,说明源代码的编码跟locale是一致的.源代码里的字符串的utf8flag同样是关闭的状态.

如果你的源代码里含有中文,那么你***遵循这个原则:1)编写代码时使用utf8编码,2)在文件的开头加上useutf8;语句.这样,你源代码里的字符串就都会是utf8编码的,并且utf8flag也已经打开.

3)从文件读入.这个毫无疑问,你的文件是什么编码,读进来就是什么编码了.读进来以后,utf8flag是off状态.

4)抓取网页.网页是什么编码就是什么编码,utf8flag是off状态.网站的编码可以从响应头里或者html的<head>标签里获得.也有可能出现响应头和htmlhead里都没说明编码的情况,这个就是做的很不礼貌的网页了.这时候只能用程序来猜:

程序代码:
 

  1. useEncode;  
  2. useLWP::Simpleqw(get);  
  3. usestrict;  
  4.  
  5. my$str=get"http://www.sina.com.cn";  
  6.  
  7. eval{my$str2=$str;Encode::decode("gbk",$str2,1)};  
  8. print"notgbk:$@\n"if$@;  
  9.  
  10. eval{my$str2=$str;Encode::decode("utf8",$str2,1)};  
  11. print"notutf8:$@\n"if$@;  
  12.  
  13. eval{my$str2=$str;Encode::decode("big5",$str2,1)};  
  14. print"notbig5:$@\n"if$@;  
  15.  

 输出:

程序代码:
 

  1. notutf8:utf8"\xD0"doesnotmaptoPerl Unicodeat/usr/local/lib/perl/5.8.8/Encode.pmline162.  
  2.  
  3. notbig5:big5-eten"\xC8"doesnotmaptoPerl Unicodeat/usr/local/lib/perl/5.8.8/Encode.pmline162.  

 我们给decode函数传递了第三个参数,要求有异常字符的时候报错.我们用eval捕获错误,转码失败说明字符串本来不是这种编码.另外注意我们每次都把$str拷贝到$str2,这是因为decode第三个参数为1时,decode以后,传给它的字符串参数(第二个参数会被清空).我们拷贝一下,这样每次被清空的都是$str2,$str不变.

来看结果,既然不是utf8,也不是big5,那就应该是gbk了.对于其他不知编码的字符串,也可以使用这种方法来猜.不过因为几种编码的内码范围都差不多,所以如果字符串比较短,就可能出不了异常字符,所以这个方法只适用于大段的文字.

输出

字符串在程序内被正确地处理后,要展现给用户.这时我们需要把字符串从perlinternalform转化成用户能接受的形式.简单地说,就是把字符串从utf8编码转换成输出的编码或表现界面的编码.这时候,我们使用$str=Encode::encode('charset',$str);.同样可以分为几种情况.

1)标准输出.标准输出的编码跟locale一致.输出的时候utf8flag应该关闭,不然就会出现我们前面看到的那行警告:

程序代码:
 

  1. WidecharacterinprintatPerl Unicode.plline10. 

2)GUI程序.这个应该是不用干什么,utf8编码,utf8flag开启就行.没有实际测试过.

3)做httppost.看网页表单要求什么编码.utf8flag开或关无所谓,因为httppost发送出去的只是字符串中的数据部分,不管utf8flag. #p#

PerlIO

PerlIO为我们的输入/输出转码提供了便利.它可以针对某个文件句柄,输入的时候自动帮你转码并开启utf8flag,输出的时候,自动帮你转码并关闭utf8flag.假设你的终端locale是gb2312,看下面的例子:

程序代码:
 

  1. usestrict;  
  2. binmode(STDIN,":encoding(gb2312)");  
  3. binmode(STDOUT,":encoding(gb2312)");  
  4. while(<>){  
  5. chomp;  
  6. print$_,length,"\n";  

运行后输入"中国",结果:

程序代码:
中国2

这样我们就省去了输入和输出时转码的麻烦.PerlIO可以作用于任何文件句柄,具体请参考perldocPerlIO.

相关API

都是Encode模块的:

$octets=encode(ENCODING,$string[,CHECK])把字符串从utf8编码转成指定的编码,并关闭utf8flag.

$string=decode(ENCODING,$octets[,CHECK])把字符串从其他编码转成utf8编码,并开启utf8flag,不过有个例外就是,如果字符串是仅仅ascii编码或EBCDIC编码的话,不开启utf8flag.

is_utf8(STRING[,CHECK])看看utf8flag是否开启.如果第二个参数为真,则同时检查编码是否符合utf8.这个检测不一定准确,跟decode方式检测效果一样.

_utf8_on(STRING)打开字符串的utfflag

_utf8_off(STRING)关闭字符串的utfflag

***两个是内部函数,不推荐使用.

参考Perl Unicode

utf8和utf-8

前面我们提到的一直都是utf8.在perl中,utf8和utf-8是不一样的.utf-8是指国际上标准的utf-8定义,而utf8是perl在国际标准上做了一些扩展,能兼容的内码要比国际标准的多一些.perlinternalform使用的是utf8.另外顺便提一下,字符集的名称是不区分大小写的并且"_"和"-"是等价的.

EBCDIC

EBCDIC是一套遗留的宽字符解决方案,不同于Perl Unicode,它不是Ascii的超集.上面介绍的方案并不完全适用于EBCDIC.关于EBCDIC,请参考perldocperlebcdic
 

【编辑推荐】

  1. Eclipse平台中Perl脚本开发
  2. Perl学习笔记----Perl命令行
  3. Perl数组和引用使用指导
  4. Perl基础 解析Perl标量和数组概念
  5. Perl模式匹配中的特殊字符用法指南

 

责任编辑:佚名 来源: csdn.net
相关推荐

2010-07-13 15:15:49

Perl二维数组

2010-05-25 15:22:30

NetBeans SV

2010-07-21 14:18:27

Perl函数

2010-07-21 13:27:06

Perl模式匹配

2010-07-21 09:50:51

Perl调试

2010-07-13 16:34:34

Perl 哈希表

2010-07-20 14:41:55

Perl语法

2010-07-19 10:01:57

Perl函数

2010-07-20 16:05:07

Perl包

2010-07-21 14:08:28

Perl命令行

2010-07-13 12:50:47

Perl变量

2010-07-13 09:50:55

Perl数组

2010-07-16 10:44:54

Perl数组

2023-11-03 09:00:12

Unicode地域障碍空间

2010-07-20 13:44:02

Perl方法

2010-07-26 14:30:29

Perl Push

2010-07-13 09:34:24

Perl纯变量

2010-07-21 13:59:59

Perl引用

2010-07-19 09:52:04

Perl标量

2010-07-13 16:20:21

Perl 哈希表
点赞
收藏

51CTO技术栈公众号