編碼 - Perl 中的字元編碼
use Encode qw(decode encode);
$characters = decode('UTF-8', $octets, Encode::FB_CROAK);
$octets = encode('UTF-8', $characters, Encode::FB_CROAK);
編碼包含一系列模組,其詳細資訊過於龐大,無法放入一個文件中。此文件本身說明了頂層 API 和一般主題的概況。如需其他主題和更多詳細資訊,請參閱這些模組的文件
Encode
模組提供 Perl 字串與系統其他部分之間的介面。Perl 字串是字元序列。
Perl 能表示的字元曲目是 Unicode Consortium 所定義字元的超集。在大部分平台上,字元的序數值(由 ord(S)
傳回)是該字元的Unicode 碼點。例外情況是傳統編碼為 EBCDIC 變體(而非 ASCII 超集)的平台;請參閱 perlebcdic。
在最近的歷史中,資料以 8 位元區塊(通常稱為「位元組」,在標準文件中也稱為「八位元組」)在電腦中傳遞。Perl 廣泛用於處理各種型別的資料:不只表示人類或電腦語言的字元字串,也包括「二進位」資料,也就是機器表示的數字、影像中的畫素,或幾乎任何東西。
當 Perl 處理「二進位資料」時,程式設計師希望 Perl 處理「位元組序列」。這對 Perl 來說不是問題:因為位元組有 256 個可能值,所以它很容易放入 Perl 更大的「邏輯字元」中。
本文件主要說明如何。 perlunitut 和 perlunifaq 說明原因。
範圍為 0 .. 2**32-1(或更多)的字元;Perl 字串的組成元素。
範圍為 0..255 的字元;Perl 字元的特殊情況。
8 位元資料,序數值為 0..255;傳遞至或從非 Perl 環境(例如磁碟檔案、標準 I/O 串流、資料庫、命令列引數、環境變數、socket 等)的位元組術語。
$octets = encode(ENCODING, STRING[, CHECK])
將純量值 字串 從 Perl 的內部格式編碼成 編碼,並傳回一個位元組序列。編碼 可以是正規名稱或別名。有關編碼名稱和別名,請參閱 "定義別名"。有關 CHECK,請參閱 "處理格式錯誤的資料"。
注意事項:輸入純量 字串 可能會根據 CHECK 中的設定進行原地修改。如果您希望輸入保持不變,請參閱 "LEAVE_SRC"。
例如,要將字串從 Perl 的內部格式轉換成 ISO-8859-1,也稱為 Latin1
$octets = encode("iso-8859-1", $string);
注意事項:當您執行 $octets = encode("UTF-8", $string)
時,$octets 可能不等於 $string。雖然兩者都包含相同的資料,但 $octets 的 UTF8 標記始終為關閉。當您編碼任何內容時,結果上的 UTF8 標記始終為關閉,即使它包含完全有效的 UTF-8 字串。請參閱以下 "UTF8 標記"。
如果 $string 為 未定義
,則傳回 未定義
。
str2bytes
可用作 encode
的別名。
$string = decode(ENCODING, OCTETS[, CHECK])
此函數傳回解碼純量值 OCTETS 的字串,假設 OCTETS 是 編碼 中的位元組序列。與 encode() 一樣,編碼 可以是正規名稱或別名。有關編碼名稱和別名,請參閱 "定義別名";有關 CHECK,請參閱 "處理格式錯誤的資料"。
注意事項:輸入純量 OCTETS 可能會根據 CHECK 中的設定進行原地修改。如果您希望輸入保持不變,請參閱 "LEAVE_SRC"。
例如,要將 ISO-8859-1 資料轉換成 Perl 內部格式的字串
$string = decode("iso-8859-1", $octets);
注意事項:當您執行 $string = decode("UTF-8", $octets)
時,$string 可能不等於 $octets。雖然兩者都包含相同的資料,但 $string 的 UTF8 標記已開啟。請參閱以下 "UTF8 標記"。
如果 $string 為 未定義
,則傳回 未定義
。
bytes2str
可用作 decode
的別名。
[$obj =] find_encoding(ENCODING)
傳回對應於 ENCODING 的 編碼物件。如果找不到符合的 ENCODING,則傳回 undef
。傳回的物件會執行實際的編碼或解碼。
$string = decode($name, $bytes);
實際上是
$string = do {
$obj = find_encoding($name);
croak qq(encoding "$name" not found) unless ref $obj;
$obj->decode($bytes);
};
並進行更多的錯誤檢查。
因此,你可以透過下列方式重複使用這個物件來節省時間;
my $enc = find_encoding("iso-8859-1");
while(<>) {
my $string = $enc->decode($_);
... # now do something with $string;
}
除了 "decode" 和 "encode" 之外,還有其他可用方法。例如,name()
會傳回編碼物件的正規名稱。
find_encoding("latin1")->name; # iso-8859-1
請參閱 Encode::Encoding 以取得詳細資料。
[$obj =] find_mime_encoding(MIME_ENCODING)
傳回對應於 MIME_ENCODING 的 編碼物件。作用與 find_encoding()
相同,但傳回物件的 mime_name()
必須與 MIME_ENCODING 相符。因此,與 find_encoding()
相反,在搜尋物件時不會使用正規名稱和別名。
find_mime_encoding("utf8"); # returns undef because "utf8" is not valid I<MIME_ENCODING>
find_mime_encoding("utf-8"); # returns encode object "utf-8-strict"
find_mime_encoding("UTF-8"); # same as "utf-8" because I<MIME_ENCODING> is case insensitive
find_mime_encoding("utf-8-strict"); returns undef because "utf-8-strict" is not valid I<MIME_ENCODING>
[$length =] from_to($octets, FROM_ENC, TO_ENC [, CHECK])
在兩個編碼之間轉換資料(原地轉換)。$octets 中的資料必須編碼為八位元組,而非 Perl 內部格式中的字元。例如,要將 ISO-8859-1 資料轉換成 Microsoft 的 CP1250 編碼
from_to($octets, "iso-8859-1", "cp1250");
並將其轉換回來
from_to($octets, "cp1250", "iso-8859-1");
由於轉換會原地進行,因此要轉換的資料不能是字串常數:它必須是純量變數。
from_to()
會在成功時傳回轉換後的字串長度(以八位元組為單位),並在發生錯誤時傳回 undef
。
注意事項:下列運算看起來相同,但實際上並非如此
from_to($data, "iso-8859-1", "UTF-8"); #1
$data = decode("iso-8859-1", $data); #2
#1 和 #2 都會讓 $data 包含一個完全有效的 UTF-8 字串,但只有 #2 會開啟 UTF8 標記。#1 等同於
$data = encode("UTF-8", decode("iso-8859-1", $data));
請參閱下方的 "UTF8 標記"。
另外請注意
from_to($octets, $from, $to, $check);
等同於
$octets = encode($to, decode($from, $octets), $check);
是的,它不會在解碼期間尊重 $check。這是故意這樣設計的。如果你需要精細的控制,請使用 decode
接著使用 encode
,如下所示
$octets = encode($to, decode($from, $octets, $check_from), $check_to);
$octets = encode_utf8($string);
警告:這個函式可能會產生無效的 UTF-8! 請勿將其用於資料交換。除非你想要 Perl 較舊的「寬鬆」模式,否則請優先使用 $octets = encode("UTF-8", $string)
。
等同於 $octets = encode("utf8", $string)
。$string 中的字元會以 Perl 的內部格式編碼,而結果會傳回為八位元組序列。由於 Perl 中所有可能的字元都有一個(寬鬆,而非嚴格)的 utf8 表示法,因此這個函式不會失敗。
$string = decode_utf8($octets [, CHECK]);
警告:此函式接受無效的 UTF-8! 請勿用於資料交換。除非您想要 Perl 較舊的「寬鬆」模式,否則請優先使用 $string = decode("UTF-8", $octets [, CHECK])
。
等同於 $string = decode("utf8", $octets [, CHECK])
。由 $octets 表示的八位元組序列會從(寬鬆的,而非嚴格的)utf8 解碼成一個邏輯字元序列。由於並非所有八位元組序列都是有效的寬鬆 utf8,因此此函式很有可能會失敗。有關 CHECK,請參閱「處理格式錯誤的資料」。
注意:輸入 $octets 可能會根據 CHECK 中的設定就地修改。如果您希望輸入保持不變,請參閱「LEAVE_SRC」。
use Encode;
@list = Encode->encodings();
傳回已載入的可用的編碼的標準名稱清單。若要取得所有可用的編碼清單,包括尚未載入的編碼,請輸入
@all_encodings = Encode->encodings(":all");
或者您可以提供特定模組的名稱
@with_jp = Encode->encodings("Encode::JP");
當名稱中沒有「::
」時,會假設為「Encode::
」
@ebcdic = Encode->encodings("EBCDIC");
若要詳細瞭解此套件支援哪些編碼,請參閱Encode::Supported。
若要將新別名新增到特定編碼,請使用
use Encode;
use Encode::Alias;
define_alias(NEWNAME => ENCODING);
之後,NEWNAME 可用作 ENCODING 的別名。ENCODING 可以是編碼的名稱或編碼物件。
在您執行此操作之前,請先使用 resolve_alias()
確定別名不存在,此函式會傳回其標準名稱。例如
Encode::resolve_alias("latin1") eq "iso-8859-1" # true
Encode::resolve_alias("iso-8859-12") # false; nonexistent
Encode::resolve_alias($name) eq $name # true if $name is canonical
resolve_alias()
不需要 use Encode::Alias
;它可以透過 use Encode qw(resolve_alias)
匯入。
有關詳細資訊,請參閱Encode::Alias。
特定編碼的標準名稱不一定要與 IANA 字元集註冊表一致,IANA 字元集註冊表通常顯示為 Content-Type: text/plain; charset=WHATEVER
。在大部分情況下,標準名稱都能正常運作,但有時無法正常運作,最著名的範例是「utf-8-strict」。
自 Encode
版本 2.21 起,因此新增一個新方法 mime_name()
。
use Encode;
my $enc = find_encoding("UTF-8");
warn $enc->name; # utf-8-strict
warn $enc->mime_name; # UTF-8
另請參閱:Encode::Encoding
如果您的 perl 支援 PerlIO
(這是預設值),您可以使用 PerlIO
層透過檔案處理來直接解碼和編碼。以下兩個範例在功能上完全相同
### Version 1 via PerlIO
open(INPUT, "< :encoding(shiftjis)", $infile)
|| die "Can't open < $infile for reading: $!";
open(OUTPUT, "> :encoding(euc-jp)", $outfile)
|| die "Can't open > $output for writing: $!";
while (<INPUT>) { # auto decodes $_
print OUTPUT; # auto encodes $_
}
close(INPUT) || die "can't close $infile: $!";
close(OUTPUT) || die "can't close $outfile: $!";
### Version 2 via from_to()
open(INPUT, "< :raw", $infile)
|| die "Can't open < $infile for reading: $!";
open(OUTPUT, "> :raw", $outfile)
|| die "Can't open > $output for writing: $!";
while (<INPUT>) {
from_to($_, "shiftjis", "euc-jp", 1); # switch encoding
print OUTPUT; # emit raw (but properly encoded) data
}
close(INPUT) || die "can't close $infile: $!";
close(OUTPUT) || die "can't close $outfile: $!";
在上面的第一個版本中,您讓適當的編碼層處理轉換。在第二個版本中,您明確地從一種編碼轉換到另一種編碼。
不幸的是,編碼可能不支援 PerlIO
。您可以透過對編碼呼叫 perlio_ok
方法,來檢查您的編碼是否受 PerlIO
支援
Encode::perlio_ok("hz"); # false
find_encoding("euc-cn")->perlio_ok; # true wherever PerlIO is available
use Encode qw(perlio_ok); # imported upon request
perlio_ok("euc-jp")
幸運的是,隨 Encode
核心附帶的所有編碼都支援 PerlIO
,但 hz
和 ISO-2022-kr
除外。有關血腥細節,請參閱 Encode::Encoding 和 Encode::PerlIO。
選用的 CHECK 參數會告訴 Encode
在遇到格式錯誤的資料時該怎麼做。如果沒有 CHECK,則假設為 Encode::FB_DEFAULT
(== 0)。
自版本 2.12 起,Encode
支援 CHECK 的 coderef 值;請參閱下方。
注意:並非所有編碼都支援此功能。有些編碼會忽略 CHECK 參數。例如,Encode::Unicode 會忽略 CHECK,而且它會在發生錯誤時發出 croak。
I<CHECK> = Encode::FB_DEFAULT ( == 0)
如果 CHECK 為 0,編碼和解碼會將任何格式錯誤的字元替換為替換字元。編碼時,會使用 SUBCHAR。解碼時,會使用 Unicode 替換字元,即碼點 U+FFFD。如果資料應該是 UTF-8,則會給出警告類別為 "utf8"
的選用詞彙警告。
I<CHECK> = Encode::FB_CROAK ( == 1)
如果 CHECK 為 1,方法會立即以錯誤訊息 die。因此,當 CHECK 為 1 時,您應該使用 eval{}
來捕捉例外,除非您真的想要讓它 die
。
I<CHECK> = Encode::FB_QUIET
如果 CHECK 設為 Encode::FB_QUIET
,則編碼和解碼會在發生錯誤時立即傳回已處理完畢的資料部分。資料參數會被該點之後的所有內容覆寫;也就是說,未處理的資料部分。當您必須重複呼叫 decode
,而您的來源資料可能包含部分多位元組字元序列(也就是說,您正在使用固定寬度緩衝區進行讀取)時,這會很方便。以下是執行此操作的範例程式碼
my($buffer, $string) = ("", "");
while (read($fh, $buffer, 256, length($buffer))) {
$string .= decode($encoding, $buffer, Encode::FB_QUIET);
# $buffer now contains the unprocessed partial character
}
I<CHECK> = Encode::FB_WARN
這與上述的 FB_QUIET
相同,但不同的是,它並非在發生錯誤時保持沉默,而是發出警告。這在您進行除錯時很方便。
警告:所有來自 Encode 模組的警告都會報告,與 pragma warnings 設定無關。如果您想遵循 pragma warnings 所設定的詞彙警告設定,請另外附加檢查值 ENCODE::ONLY_PRAGMA_WARNINGS
。此值自 Encode 2.99 版起提供。
對於由 Encode::XS
模組實作的編碼,CHECK
==
Encode::FB_PERLQQ
會將 encode
和 decode
置入 perlqq
備用模式。
當您解碼時,會為格式錯誤的字元插入 \xHH
,其中 HH 是無法解碼為 utf8 的八位元組的十六進位表示。當您編碼時,會插入 \x{HHHH}
,其中 HHHH 是編碼字元曲目中找不到的字元的 Unicode 碼點(以任意數量的十六進位數字表示)。
HTML/XML 字元參考模式大致相同。HTML 使用 &#NNN;
取代 \x{HHHH}
,其中 NNN 是十進位數字,而 XML 使用 &#xHHHH;
,其中 HHHH 是十六進位數字。
在 Encode
2.10 或更新版本中,也暗示了 LEAVE_SRC
。
這些模式實際上都是透過位元遮罩設定的。以下是 FB_XXX
常數的配置方式。您可以透過 use Encode qw(:fallbacks)
匯入 FB_XXX
常數,並可透過 use Encode qw(:fallback_all)
匯入一般位元遮罩常數。
FB_DEFAULT FB_CROAK FB_QUIET FB_WARN FB_PERLQQ
DIE_ON_ERR 0x0001 X
WARN_ON_ERR 0x0002 X
RETURN_ON_ERR 0x0004 X X
LEAVE_SRC 0x0008 X
PERLQQ 0x0100 X
HTMLCREF 0x0200
XMLCREF 0x0400
Encode::LEAVE_SRC
如果 Encode::LEAVE_SRC
位元未設定,但 CHECK 已設定,則會覆寫要編碼或解碼的來源字串。如果您對此不感興趣,請將其與位元遮罩進行位元或運算。
自 Encode
2.12 起,CHECK
也可能是程式碼參考,它會將未對應字元的序數值作為引數,並傳回表示備用字元的八位元組。例如
$ascii = encode("ascii", $utf8, sub{ sprintf "<U+%04X>", shift });
作用類似 FB_PERLQQ
,但使用 U+XXXX 而不是 \x{XXXX}
。
decode
的備用方案必須傳回已解碼的字串(字元序列),並以序數值清單作為其引數。因此,例如,如果您希望將八位元組解碼為 UTF-8,並使用 ISO-8859-15 作為非有效 UTF-8 位元組的備用方案,您可以撰寫
$str = decode 'UTF-8', $octets, sub {
my $tmp = join '', map chr, @_;
return decode 'ISO-8859-15', $tmp;
};
若要定義新的編碼,請使用
use Encode qw(define_encoding);
define_encoding($object, CANONICAL_NAME [, alias...]);
CANONICAL_NAME 會與 $object 關聯。物件應提供 Encode::Encoding 中所述的介面。如果提供兩個以上的引數,則其他引數會被視為 $object 的別名。
請參閱 Encode::Encoding 以取得詳細資料。
在 Perl 中導入 Unicode 支援之前,eq
算子只會比較兩個純量所表示的字串。從 Perl 5.8 開始,eq
會同時考量 UTF8 標記 來比較兩個字串。為了說明我們這樣做的原因,我引用 Programming Perl, 3rd ed. 第 402 頁的內容。
舊的位元組導向程式不應在它們過去處理的舊位元組導向資料上突然中斷。
舊的位元組導向程式應在適當的時候神奇地開始處理新的字元導向資料。
程式在新的字元導向模式下執行時,速度應與舊的位元組導向模式一樣快。
Perl 應保持為單一語言,而不是分岔為位元組導向 Perl 和字元導向 Perl。
在撰寫 Programming Perl, 3rd ed. 時,甚至連 Perl 5.6.0 都尚未問世,書中記載的許多功能在很長一段時間內都未實作。Perl 5.8 修正了這項缺失,而導入 UTF8 標記就是其中之一。您可以想像 Perl 中有兩種根本不同的字串和字串運算:一種是在內部 UTF8 標記關閉時的位元組導向模式,另一種是在內部 UTF8 標記開啟時的字元導向模式。
這個 UTF8 標記在 Perl 腳本中不可見,原因與您無法(或更確切地說,您 不必)看到純量包含字串、整數或浮點數相同。但如果您願意,您仍然可以窺探和戳戳它們。請參閱下一節。
下列 API 在目前的實作中使用 Perl 內部運作的一部分。因此,它們很有效率,但可能會在未來的版本中變更。
is_utf8(STRING [, CHECK])
[內部] 測試 字串 中的 UTF8 標記是否開啟。如果 檢查 為真,也會檢查 字串 是否包含格式良好的 UTF-8。如果成功,傳回真,否則傳回假。
通常只在除錯和測試時需要。不要將此標記用於區分字元和二進位資料,這應該在撰寫程式碼時為每個變數決定。
注意:如果 字串 已設定 UTF8 標記,並不表示 字串 已編碼為 UTF-8,反之亦然。
從 Perl 5.8.1 開始,utf8 也有 utf8::is_utf8
函式。
_utf8_on(STRING)
[內部] 將 字串 的內部 UTF8 標記開啟。字串 不會 檢查是否只包含格式良好的 UTF-8。除非你絕對確定字串只包含格式良好的 UTF-8,否則不要使用此函式。傳回 UTF8 標記的前一個狀態(因此請不要將傳回值視為成功或失敗的指標),或 未定義
(如果 字串 不是字串)。
注意:基於安全性考量,此函式不適用於已污染的值。
_utf8_off(STRING)
[內部] 將 字串 的內部 UTF8 標記關閉。不要輕易使用。傳回 UTF8 標記的前一個狀態,或 未定義
(如果 字串 不是字串)。不要將傳回值視為成功或失敗的指標,因為這不是它的意思:它只是前一個設定。
注意:基於安全性考量,此函式不適用於已污染的值。
....We now view strings not as sequences of bytes, but as sequences
of numbers in the range 0 .. 2**32-1 (or in the case of 64-bit
computers, 0 .. 2**64-1) -- Programming Perl, 3rd ed.
這在歷史上一直是 Perl 對 UTF-8 的概念,因為這是肯·湯普森在發明 UTF-8 時最初構想的方式。然而,由於後來對適用標準的修訂,官方的 UTF-8 現在比這嚴格得多。例如,它的範圍窄得多(0 .. 0x10_FFFF 僅涵蓋 21 位元,而不是 32 或 64 位元),並且不允許某些序列,例如用於代理對、31 個非字元碼點 0xFDD0 .. 0xFDEF、任何 平面中的最後兩個碼點(0xXX_FFFE 和 0xXX_FFFF)、所有非最短編碼等。
Perl 以前總是使用寬鬆的 UTF-8 解釋的預設值現在已被推翻
From: Larry Wall <larry@wall.org>
Date: December 04, 2004 11:51:58 JST
To: perl-unicode@perl.org
Subject: Re: Make Encode.pm support the real UTF-8
Message-Id: <20041204025158.GA28754@wall.org>
On Fri, Dec 03, 2004 at 10:12:12PM +0000, Tim Bunce wrote:
: I've no problem with 'utf8' being perl's unrestricted uft8 encoding,
: but "UTF-8" is the name of the standard and should give the
: corresponding behaviour.
For what it's worth, that's how I've always kept them straight in my
head.
Also for what it's worth, Perl 6 will mostly default to strict but
make it easy to switch back to lax.
Larry
了解了嗎?自 Perl 5.8.7 起,「UTF-8」表示當前意義上的 UTF-8,它保守、嚴謹且注重安全性,而「utf8」表示以前意義上的 UTF-8,它自由、寬鬆且不嚴謹。因此,Encode
2.10 或更新版本理解 「UTF-8」
和 「utf8」
之間的細微但至關重要的區別。
encode("utf8", "\x{FFFF_FFFF}", 1); # okay
encode("UTF-8", "\x{FFFF_FFFF}", 1); # croaks
此區別對解碼也很重要。以下範例中,$s
儲存字元 U+200000,它超過 UTF-8 允許的範圍。因此,$s
儲存無效的 Unicode 編碼點
$s = decode("utf8", "\xf8\x88\x80\x80\x80");
相對地,「UTF-8」
會將輸入強制轉換為有效內容
$s = decode("UTF-8", "\xf8\x88\x80\x80\x80"); # U+FFFD
.. 或中斷
decode("UTF-8", "\xf8\x88\x80\x80\x80", FB_CROAK|LEAVE_SRC);
在 Encode
模組中,「UTF-8」
實際上是 「utf-8-strict」
的正規名稱。「UTF」
和 「8」
之間的連字號至關重要;沒有它,Encode
會變得「自由」且(可能過度)寬容
find_encoding("UTF-8")->name # is 'utf-8-strict'
find_encoding("utf-8")->name # ditto. names are case insensitive
find_encoding("utf_8")->name # ditto. "_" are treated as "-"
find_encoding("UTF8")->name # is 'utf8'.
Perl 的內部 UTF8 標記稱為「UTF8」,沒有連字號。它表示字串是否內部編碼為「utf8」,也沒有連字號。
Encode::Encoding、Encode::Supported、Encode::PerlIO、encoding、perlebcdic、"open" in perlfunc、perlunicode、perluniintro、perlunifaq、perlunitut utf8、Perl Unicode 郵件清單 http://lists.perl.org/list/perl-unicode.html
此專案最初由已故的 Nick Ing-Simmons 發起,後來由 Dan Kogai <dankogai@cpan.org> 維護。請參閱 AUTHORS,取得參與人員的完整清單。如有任何問題,請寄信至 <perl-unicode@perl.org>,以便我們都能分享。
雖然 Dan Kogai 作為維護人員保留著作權,但應歸功於所有參與者。請參閱 AUTHORS,取得提交程式碼給專案的人員清單。
著作權所有 2002-2014 Dan Kogai <dankogai@cpan.org>。
此程式庫是免費軟體;您可以在與 Perl 相同的條款下重新散布或修改它。