日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

扶凱 ? Perl Unicode全攻略(轉(zhuǎn))

 閑來看看 2011-09-22

Perl Unicode全攻略(轉(zhuǎn))

Dec 13th, 2008

轉(zhuǎn)載本站文章請注明,轉(zhuǎn)載自:扶凱[http://www.]

本文鏈接: http://www./2008/12/13/perl-unicode.html

轉(zhuǎn)自:http://blog./u2/70049/showart_1210487.html

耐心看完本文, 相信你今后在unicode處理上不會(huì)再有什么問題.

本文內(nèi)容適用于perl 5.8及其以上版本.

perl internal form
在Perl看來, 字符串只有兩種形式. 一種是octets, 即8位序列, 也就是我們通常說的字節(jié)數(shù)組. 另一種utf8編碼的字符串, perl管它叫string. 也就是說: Perl只認(rèn)識(shí)兩種編碼: Ascii(octets)和utf8(string).

utf8 flag

那么perl如何確定一個(gè)字符串是octets還是utf8編碼的字符串呢? perl可沒有什么智能, 他完全是靠字符串上的utf8 flag. 在perl內(nèi)部, 字符串結(jié)構(gòu)由兩部分組成: 數(shù)據(jù)和utf8 flag. 比如字符串"中國"在perl內(nèi)部的存儲(chǔ)是這樣:

utf8 flag    數(shù)據(jù)
On   中國

如果utf8 flag是On的話, perl就會(huì)把中國當(dāng)成utf8字符串來處理, 如果utf8 flag為Off, perl就會(huì)把他當(dāng)成octets來處理. 所有字符串相關(guān)的函數(shù)包括正則表達(dá)式都會(huì)受utf8 flag的影響. 讓我們來看個(gè)例子:

程序代碼:
use Encode;
use strict;

my $str = "中國";
Encode::_utf8_on($str);
print length($str) . "\n";
Encode::_utf8_off($str);
print length($str) . "\n";

運(yùn)行結(jié)果是:

程序代碼:
2
6

這里我們使用Encode模塊的_utf8_on函數(shù)和_utf8_off函數(shù)來開關(guān)字符串"中國"的utf8 flag. 可以看到, utf8 flag打開的時(shí)候, "中國"被當(dāng)成utf8字符串處理, 所以其長度是2. utf8 flag關(guān)閉的時(shí)候, "中國"被當(dāng)成octets(字節(jié)數(shù)組)處理, 出來的長度是6(我的編輯器用的是utf8編碼, 如果你的編輯器用的是gb2312編碼, 那么長度應(yīng)該是4).

再來看看正則表達(dá)式的例子:

程序代碼:
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";

運(yùn)行結(jié)果:

程序代碼:
Wide character in print at unicode.pl line 10.
china中國
china

結(jié)果第一行是一條警告, 這個(gè)我們稍后再討論. 結(jié)果的第二行說明, utf8 flag開啟的情況下, 正則表達(dá)式中的\w能夠匹配中文, 反之則不能.

如何確定一個(gè)字符串的utf8 flag是否已開啟? 使用Encode::is_utf8($str). 這個(gè)函數(shù)并不是用來檢測一個(gè)字符串是不是utf8編碼, 而是僅僅看看它的utf8 flag是否開啟.

eq

eq是一個(gè)字符串比較操作符, 只有當(dāng)字符串的內(nèi)容一致并且utf8 flag的狀態(tài)也是一致的時(shí)候, eq才會(huì)返回真.

理論就是上面這些, 一定要搞明白, 記清楚! 下面是實(shí)際應(yīng)用.

unicode轉(zhuǎn)碼

如果你有一個(gè)字符串"中國", 它是gb2312編碼的. 如果它的utf8 flag是關(guān)閉的, 它就會(huì)被當(dāng)成octets來處理, length()會(huì)返回4, 這通常不是你想要的. 而如果你開啟它的utf8 flag, 則它會(huì)被當(dāng)做utf8編碼的字符串來處理. 由于它本來的編碼是gb2312的, 不是utf8的, 這就可能導(dǎo)致錯(cuò)誤發(fā)生. 由于gb2312和utf8內(nèi)碼范圍部分重疊, 所以很多時(shí)候, 不會(huì)有錯(cuò)誤報(bào)出來, 但是perl可能已經(jīng)錯(cuò)誤地拆解了字符. 嚴(yán)重的時(shí)候, perl會(huì)報(bào)警, 說某個(gè)字節(jié)不是合法的utf8內(nèi)碼.

解決的方法很顯然, 如果你的字符串本來不是utf8編碼的, 應(yīng)該先把它轉(zhuǎn)成utf8編碼, 并且使它的utf8 flag處于開啟狀態(tài). 對(duì)于一個(gè)gb2312編碼的字符串, 你可以使用

程序代碼:
$str = Encode::decode("gb2312", $str);

來將其轉(zhuǎn)化為utf8編碼并開啟utf8 flag. 如果你的字符串編碼本來就是utf8, 只是utf8 flag沒有打開, 那么你可以使用以下三種方式中的任一種來開啟utf8 flag:

程序代碼:
$str = Encode::decode_utf8($str);
$str = Encode::decode("utf8", $str);
Encode::_utf8_on($str);

最后一種方式效率最高, 但是官方不推薦. 以下劃線開頭的函數(shù)是內(nèi)部函數(shù), 出于禮貌, 一般不從外部調(diào)用.

字符串連接

. 是字符串連接操作符. 連接兩個(gè)字符串時(shí), 如果兩個(gè)字符串的utf8 flag都是Off, 那么結(jié)果字符串也是Off. 如果其中任何一個(gè)字符串的utf8 flag是On的話, 那么結(jié)果字符串的utf8 flag將是On. 連接字符串并不會(huì)改變它們原來的編碼, 所以如果你把兩個(gè)不同編碼的字符串連在一起, 那么以后不管對(duì)這個(gè)字符串怎么轉(zhuǎn)碼, 都總會(huì)有一段是亂碼. 這種情況一定要避免, 連接兩個(gè)字符串之前應(yīng)該確保它們編碼一致. 如有必要, 先進(jìn)行轉(zhuǎn)碼, 再連接字符串.

perl unicode編程基本原則

對(duì)于任何要處理的unicode字符串, 1)把它的編碼轉(zhuǎn)換成utf8; 2)開啟它的utf8 flag

字符串來源

為了應(yīng)用上面說到的基本原則, 我們首先要知道字符串本來的編碼和utf8 flag開關(guān)情況, 這里我們討論幾種情況.

1) 命令行參數(shù)和標(biāo)準(zhǔn)輸入. 從命令行參數(shù)或標(biāo)準(zhǔn)輸入(STDIN)來的字符串, 它的編碼跟locale有關(guān). 如果你的locale是zh_CN或zh_CN.gb2312, 那么進(jìn)來的字符串就是gb2312編碼, 如果你的locale是zh_CN.gbk, 那么進(jìn)來的編碼就是gbk, 如果你的編碼是zh_CN.UTF8, 那進(jìn)來的編碼就是utf8. 不管是什么編碼, 進(jìn)來的字符串的utf8 flag都是關(guān)閉的狀態(tài).

2) 你的源代碼里的字符串. 這要看你編寫源代碼時(shí)用的是什么編碼. 在editplus里, 你可以通過"文件"->"另存為"查看和更改編碼. 在linux下, 你可以cat一個(gè)源代碼文件, 如果中文正常顯示, 說明源代碼的編碼跟locale是一致的. 源代碼里的字符串的utf8 flag同樣是關(guān)閉的狀態(tài).

如果你的源代碼里含有中文, 那么你最好遵循這個(gè)原則: 1) 編寫代碼時(shí)使用utf8編碼, 2)在文件的開頭加上use utf8;語句. 這樣, 你源代碼里的字符串就都會(huì)是utf8編碼的, 并且utf8 flag也已經(jīng)打開.

3) 從文件讀入. 這個(gè)毫無疑問, 你的文件是什么編碼, 讀進(jìn)來就是什么編碼了. 讀進(jìn)來以后, utf8 flag是off狀態(tài).

4) 抓取網(wǎng)頁. 網(wǎng)頁是什么編碼就是什么編碼, utf8 flag是off狀態(tài). 網(wǎng)站的編碼可以從響應(yīng)頭里或者h(yuǎn)tml的<head>標(biāo)簽里獲得. 也有可能出現(xiàn)響應(yīng)頭和html head里都沒說明編碼的情況, 這個(gè)就是做的很不禮貌的網(wǎng)頁了. 這時(shí)候只能用程序來猜:

程序代碼:
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", $str2, 1)};
print "not utf8: $@\n" if $@;

eval {my $str2 = $str; Encode::decode("big5", $str2, 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.

not big5: big5-eten "\xC8" does not map to Unicode at /usr/local/lib/perl/5.8.8/Encode.pm line 162.

我們給decode函數(shù)傳遞了第三個(gè)參數(shù), 要求有異常字符的時(shí)候報(bào)錯(cuò). 我們用eval捕獲錯(cuò)誤, 轉(zhuǎn)碼失敗說明字符串本來不是這種編碼. 另外注意我們每次都把$str拷貝到$str2, 這是因?yàn)閐ecode第三個(gè)參數(shù)為1時(shí), decode以后, 傳給它的字符串參數(shù)(第二個(gè)參數(shù)會(huì)被清空). 我們拷貝一下, 這樣每次被清空的都是$str2, $str不變.

來看結(jié)果, 既然不是utf8, 也不是big5, 那就應(yīng)該是gbk了. 對(duì)于其他不知編碼的字符串, 也可以使用這種方法來猜. 不過因?yàn)閹追N編碼的內(nèi)碼范圍都差不多, 所以如果字符串比較短, 就可能出不了異常字符, 所以這個(gè)方法只適用于大段的文字.

輸出

字符串在程序內(nèi)被正確地處理后, 要展現(xiàn)給用戶. 這時(shí)我們需要把字符串從perl internal form轉(zhuǎn)化成用戶能接受的形式. 簡單地說, 就是把字符串從utf8編碼轉(zhuǎn)換成輸出的編碼或表現(xiàn)界面的編碼. 這時(shí)候, 我們使用$str = Encode::encode(‘charset’, $str);. 同樣可以分為幾種情況.

1) 標(biāo)準(zhǔn)輸出. 標(biāo)準(zhǔn)輸出的編碼跟locale一致. 輸出的時(shí)候utf8 flag應(yīng)該關(guān)閉, 不然就會(huì)出現(xiàn)我們前面看到的那行警告:

程序代碼:
Wide character in print at unicode.pl line 10.

2) GUI程序. 這個(gè)應(yīng)該是不用干什么, utf8編碼, utf8 flag開啟就行. 沒有實(shí)際測試過.

3) 做http post. 看網(wǎng)頁表單要求什么編碼. utf8 flag開或關(guān)無所謂, 因?yàn)閔ttp post發(fā)送出去的只是字符串中的數(shù)據(jù)部分, 不管utf8 flag.

PerlIO

PerlIO為我們的輸入/輸出轉(zhuǎn)碼提供了便利. 它可以針對(duì)某個(gè)文件句柄, 輸入的時(shí)候自動(dòng)幫你轉(zhuǎn)碼并開啟utf8 flag, 輸出的時(shí)候, 自動(dòng)幫你轉(zhuǎn)碼并關(guān)閉utf8 flag. 假設(shè)你的終端locale是gb2312, 看下面的例子:

程序代碼:
use strict;
binmode(STDIN, ":encoding(gb2312)");
binmode(STDOUT, ":encoding(gb2312)");
while (<>) {
chomp;
print $_, length, "\n";
}

運(yùn)行后輸入"中國", 結(jié)果:

程序代碼:
中國2

這樣我們就省去了輸入和輸出時(shí)轉(zhuǎn)碼的麻煩. PerlIO可以作用于任何文件句柄, 具體請參考perldoc PerlIO.

相關(guān)API

都是Encode模塊的:

$octets = encode(ENCODING, $string [, CHECK]) 把字符串從utf8編碼轉(zhuǎn)成指定的編碼, 并關(guān)閉utf8 flag.

$string = decode(ENCODING, $octets [, CHECK]) 把字符串從其他編碼轉(zhuǎn)成utf8編碼, 并開啟utf8 flag, 不過有個(gè)例外就是, 如果字符串是僅僅ascii編碼或EBCDIC編碼的話, 不開啟utf8 flag.

is_utf8(STRING [, CHECK]) 看看utf8 flag是否開啟. 如果第二個(gè)參數(shù)為真, 則同時(shí)檢查編碼是否符合utf8. 這個(gè)檢測不一定準(zhǔn)確, 跟decode方式檢測效果一樣.

_utf8_on(STRING) 打開字符串的utf flag

_utf8_off(STRING) 關(guān)閉字符串的utf flag

最后兩個(gè)是內(nèi)部函數(shù), 不推薦使用.

參考perldoc Encode.

utf8和utf-8

前面我們提到的一直都是utf8. 在perl中, utf8和utf-8是不一樣的. utf-8是指國際上標(biāo)準(zhǔn)的utf-8定義, 而utf8是perl在國際標(biāo)準(zhǔn)上做了一些擴(kuò)展, 能兼容的內(nèi)碼要比國際標(biāo)準(zhǔn)的多一些. perl internal form使用的是utf8. 另外順便提一下, 字符集的名稱是不區(qū)分大小寫的并且"_"和"-"是等價(jià)的.

EBCDIC

EBCDIC是一套遺留的寬字符解決方案, 不同于unicode, 它不是Ascii的超集. 上面介紹的方案并不完全適用于EBCDIC. 關(guān)于EBCDIC, 請參考perldoc perlebcdic

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多