讨论perl中的utf-8编码处理

 

为了比较方便,考虑这样一个应用:把html页面中的所有非汉字字符全部去掉。

这里顺便告诉大家一个秘诀,只要文本被perl 按正确编码解释后,利用/w就可以匹配一个字母、数字、_、汉字,这个特性是不是很方便,所以我们只要用如下两次正则表达式就可以去掉所有非汉字字符,包括全角的一些标点(@#$%<,())也能去的很干净:

 

$str   =~  s / [ ^ w] // g;
$str   =~  s / [ 0 - 9a - zA - Z_] // g;

 

 问题在于如何让perl正确的理解我们的文本,我先给出我们的测试程序如下:

 

# ! /usr/bin/perl 
use  strict;
use  Encode;
use   open  IN  =>   " :raw " ,    OUT  =>   " :raw " ;

my   $arg   =   $ARGV [ 0 ];
sub  aaa {
        
open  FH ,   " testutf8 "  or  die   " aaa$! " ;
        
local  $ /   =   undef ;
        
binmode  FH ,   " :utf8 " ;
        
my   $str   =   < FH > ;
        
return   $str ;
}
sub  bbb {
        
open  FH ,   " testutf8 " ;
        
local  $ /   =   undef ;
        
binmode  FH ,   " :raw " ;
        
my   $str   =   < FH > ;
        
$str   =   pack   " U0C* " ,   unpack   " C* " ,   $str ;
        
return   $str ;
}
sub  ccc {
        
open  FH ,   " testutf8 "  or  die   " aaa$! " ;
        
local  $ /   =   undef ;
        
binmode  FH ,   " :raw " ;
        
my   $str   =   < FH > ;
        
$str   =  Encode :: decode_utf8( $str );
        
return   $str ;          
}
sub  ddd {
        
open  FH ,   " testutf8 "  or  die   " aaa$! " ;
        
local  $ /   =   undef ;     
        
binmode  FH ,   " :raw " ;   
        
my   $str   =   < FH > ;       
        
$str   =  decode( ' utf-8 ' ,   $str );  
        
return   $str ;
}
sub  eee {
        
open  FH ,   " testgb "  or  die   " aaa$! " ;
        
local  $ /   =   undef ;
        
binmode  FH ,   " :raw " ;
        
my   $str   =   < FH > ;
        Encode
:: from_to( $str ,   ' gbk ' ,   ' utf-8 ' );
        
$str   =  Encode :: decode_utf8( $str );
        
return   $str ;
}
sub  fff {
        
my   $str   =  `iconv  - f gbk  - t utf - 8  testgb`;
        
$str   =  Encode :: decode_utf8( $str );
        
return   $str ;
}
my   $f ;
eval ( " $f = *$arg " );
for  ( my   $i   =   0 $i   <   200 $i ++ ) {
        
my   $str   =   & $f ();
        
# print "$i ",(length $str)." ";
}
my   $str ;
$str   =   & $f ();
$str   =~  s / [ ^ w] // g;
$str   =~  s / [ 0 - 9a - zA - Z_] // g;
print   $str ;

 

这里共有aaa-fff六个方法,其中aaa-ddd四个方法是把一个叫testutf8的文本文件读入并转码,而eee-fff两个方法是读入testgb文本的。

程序运行的时候,第一个参数传入方法名,像这样./test.pl aaa就可以了,在命令前面加time可以统计所花的时间。这里为了避免perl做的干扰,程序一开始用use open IN => ":raw",   OUT => ":raw"; 强制默认的输入输出都不做解释。

这六种方法都是经过测试能正确得到要求的结果的,但是运行速度在我的perl 5.8.0下却是不一样的,如下

aaa    0m0.376s
bbb    0m5.263s
ccc    0m0.432s
ddd    0m2.668s
eee    0m0.784s
fff    0m1.358s

从结果我们可以看出,方法bbb最慢,它使用了某些文章上推崇的pack、unpack技巧,不仅从语法上来说极其恶心,效率也是最差
而方法ddd和ccc同样使用了Encode模块,做了同样的事,效率却差的很大,可见Encode模块还是存在缺陷的。也就是说decode方法比decode_utf8方法慢的太多了。
方法aaa并没有太出色的表现,不过也居第一,说明perl底层对utf-8的直接支持还算行。
作为一个反映perl底层糟糕的例子,把":utf8"改成":encoding(utf-8)"测试一下,用了0m1.650s,慢了400%

作为更多的尝试,考虑一下GBK编码的例子,eee使用了一个迂回战术,先用from_to转成utf-8,然后直接调用decode_utf8以避开decode的调用,fff类似,只不过调用了iconv进程来转。
从这我们看出,linux的进程生成代价果然极低,fff方法的差距竟然比bbb的更小(意思是,perl中的不当编程,导致竟然比反复调用外部程序做的还慢)。

那么decode是否真的很慢呢,把eee方法改动一下,直接调用encode,像这样
$str = Encode::decode('gbk', $str);
我们发现,时间是0m0.727s,也就是说,还是比迂回的方法快。

这种奇怪的现象怎么解释呢,
我懒得去研读Encode模块的实现了,只先这么推测吧:

gbk的解码比utf-8要容易得多,所以参数为gbk时的decode方法很快,而参数为utf-8就很慢了。
为什么直接用decode_utf8就这么快呢(比encode用gbk参数还快),只能说,perl对它内置的东西还是做了相当的优化,效率并不低。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值