您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關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的影響.讓我們來看個例子:
程序代碼:
useEncode; usestrict; my$str="中國"; Encode::_utf8_on($str); printlength($str)."\n"; Encode::_utf8_off($str); printlength($str)."\n";
運行結果是:
程序代碼:
2
6
這里我們使用Encode模塊的_utf8_on函數和_utf8_off函數來開關字符串"中國"的utf8flag.可以看到,utf8flag打開的時候,"中國"被當成utf8字符串處理,所以其長度是2.utf8flag關閉的時候,"中國"被當成octets(字節數組)處理,出來的長度是6(我的編輯器用的是utf8編碼,如果你的編輯器用的是gb2312編碼,那么長度應該是4).
再來看看正則表達式的例子:
程序代碼:
useEncode; usestrict; 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";
運行結果:
程序代碼:
WidecharacterinprintatPerl Unicode.plline10.
china中國
china
結果***行是一條警告,這個我們稍后再討論.結果的第二行說明,utf8flag開啟的情況下,正則表達式中的\w能夠匹配中文,反之則不能.
如何確定一個字符串的utf8flag是否已開啟?使用Encode::is_utf8($str).這個函數并不是用來檢測一個字符串是不是utf8編碼,而是僅僅看看它的utf8flag是否開啟.
eq
eq是一個字符串比較操作符,只有當字符串的內容一致并且utf8flag的狀態也是一致的時候,eq才會返回真.
理論就是上面這些,一定要搞明白,記清楚!下面是實際應用.
Perl Unicode轉碼
如果你有一個字符串"中國",它是gb2312編碼的.如果它的utf8flag是關閉的,它就會被當成octets來處理,length()會返回4,這通常不是你想要的.而如果你開啟它的utf8flag,則它會被當做utf8編碼的字符串來處理.由于它本來的編碼是gb2312的,不是utf8的,這就可能導致錯誤發生.由于gb2312和utf8內碼范圍部分重疊,所以很多時候,不會有錯誤報出來,但是perl可能已經錯誤地拆解了字符.嚴重的時候,perl會報警,說某個字節不是合法的utf8內碼.
解決的方法很顯然,如果你的字符串本來不是utf8編碼的,應該先把它轉成utf8編碼,并且使它的utf8flag處于開啟狀態.對于一個gb2312編碼的字符串,你可以使用
程序代碼:
$str=Encode::decode("gb2312",$str);
來將其轉化為utf8編碼并開啟utf8flag.如果你的字符串編碼本來就是utf8,只是utf8flag沒有打開,那么你可以使用以下三種方式中的任一種來開啟utf8flag:
程序代碼:
$str=Encode::decode_utf8($str); $str=Encode::decode("utf8",$str); 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里都沒說明編碼的情況,這個就是做的很不禮貌的網頁了.這時候只能用程序來猜:
程序代碼:
useEncode; useLWP::Simpleqw(get); usestrict; my$str=get"http://www.sina.com.cn"; eval{my$str2=$str;Encode::decode("gbk",$str2,1)}; print"notgbk:$@\n"if$@; eval{my$str2=$str;Encode::decode("utf8",$str2,1)}; print"notutf8:$@\n"if$@; eval{my$str2=$str;Encode::decode("big5",$str2,1)}; print"notbig5:$@\n"if$@;
輸出:
程序代碼:
notutf8:utf8"\xD0"doesnotmaptoPerl Unicodeat/usr/local/lib/perl/5.8.8/Encode.pmline162. 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應該關閉,不然就會出現我們前面看到的那行警告:
程序代碼:
WidecharacterinprintatPerl Unicode.plline10.
2)GUI程序.這個應該是不用干什么,utf8編碼,utf8flag開啟就行.沒有實際測試過.
3)做httppost.看網頁表單要求什么編碼.utf8flag開或關無所謂,因為httppost發送出去的只是字符串中的數據部分,不管utf8flag.
PerlIO
PerlIO為我們的輸入/輸出轉碼提供了便利.它可以針對某個文件句柄,輸入的時候自動幫你轉碼并開啟utf8flag,輸出的時候,自動幫你轉碼并關閉utf8flag.假設你的終端locale是gb2312,看下面的例子:
程序代碼:
usestrict; binmode(STDIN,":encoding(gb2312)"); binmode(STDOUT,":encoding(gb2312)"); while(<>){ chomp; 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
關于“Perl Unicode的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。