為了比較方便,考慮這樣一個應用:把html頁面中的所有非漢字字元全部去掉。
這裡順便告訴大家一個秘訣,只要文本被perl 按正確編碼解釋後,利用/w就可以匹配一個字母、數字、_、漢字,這個特性是不是很方便,所以我們只要用如下兩次Regex就可以去掉所有非漢字字元,包括全形的一些標點(@#$%<,())也能去的很乾淨:
$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對它內建的東西還是做了相當的最佳化,效率並不低。