內容

名稱

Encode::PerlIO -- Encode 和 PerlIO 的詳細文件

概述

在讀取或寫入檔案、網路連線、管線等時,通常會想要進行編碼轉換。如果 Perl 設定為使用新的「perlio」IO 系統,則 Encode 會提供一個「層級」(請參閱 PerlIO),可以在讀取或寫入資料時轉換資料。

以下是盲詩人如何現代化編碼

use Encode;
open(my $iliad,'<:encoding(iso-8859-7)','iliad.greek');
open(my $utf8,'>:utf8','iliad.utf8');
my @epic = <$iliad>;
print $utf8 @epic;
close($utf8);
close($illiad);

此外,新的 IO 系統也可以設定為讀取/寫入 UTF-8 編碼字元(如上所述,這很有效率)

open(my $fh,'>:utf8','anything');
print $fh "Any \x{0021} string \N{SMILEY FACE}\n";

上述任一種形式的「層級」規格都可以使用 use open ... pragma 成為詞彙範圍的預設值。請參閱 open

開啟處理常式後,可以使用 binmode 變更其層級。

如果沒有任何此類設定,或 Perl 本身是使用系統自己的 IO 建置的,則寫入操作會假設檔案控制代碼只接受位元組,如果寫入控制代碼的字元大於 255,則會die。在讀取時,控制代碼中的每個八位元組都會變成字元中的位元組。請注意,此預設值與只有位元組的語言(包括 v5.6 之前的 Perl)的行為相同,足以處理原生 8 位元編碼,例如 iso-8859-1、EBCDIC 等,以及處理其他編碼和二進位資料的任何舊有機制。

在其他情況下,程式有責任在執行寫入之前使用上述 API 將字元轉換為位元組,並在執行「字元操作」(例如 lc/\W+/、...)之前將從控制代碼讀取的位元組轉換為字元。

您也可以使用 PerlIO 轉換您不想載入記憶體的大量資料。例如,要在 ISO-8859-1(拉丁文 1)和 UTF-8(或 EBCDIC 電腦中的 UTF-EBCDIC)之間轉換

open(F, "<:encoding(iso-8859-1)", "data.txt") or die $!;
open(G, ">:utf8",                 "data.utf") or die $!;
while (<F>) { print G }

# Could also do "print G <F>" but that would pull
# the whole file into memory just to write it out again.

更多範例

open(my $f, "<:encoding(cp1252)")
open(my $g, ">:encoding(iso-8859-2)")
open(my $h, ">:encoding(latin9)")       # iso-8859-15

另請參閱 編碼,了解如何變更指令碼中資料的預設編碼。

它是如何運作的?

以下是檔案控制代碼、PerlIO 和 Encode 如何互動的粗略圖表。

filehandle <-> PerlIO        PerlIO <-> scalar (read/printed)
                     \      /
                      Encode   

當 PerlIO 從任一方向接收資料時,它會填滿一個緩衝區(目前為 1024 位元組),並將緩衝區傳遞給 Encode。Encode 嘗試轉換有效的部分,並將其傳遞回 PerlIO,將無效的部分(通常是部分字元)留在緩衝區中。然後,PerlIO 將更多資料附加到緩衝區,再次呼叫 Encode,依此類推,直到資料串流結束。

為此,PerlIO 總是呼叫將 CHECK 設定為 1 的(de|en)code 方法。這可確保方法在遇到部分字元時在正確的位置停止。以下是 PerlIO 和 Encode 嘗試編碼(從 utf8)超過 1024 位元組,而緩衝區邊界碰巧位於字元中間時所發生的事情。

 A   B   C   ....   ~     \x{3000}    ....
41  42  43   ....  7E   e3   80   80  ....
<- buffer --------------->
<< encoded >>>>>>>>>>
                     <- next buffer ------

Encode 從開頭轉換到 \x7E,將 \xe3 留存在緩衝區中,因為它是無效的(部分字元)。

不幸的是,此方案不適用於基於跳脫的編碼,例如 ISO-2022-JP。

行緩衝

現在讓我們看看當您嘗試從 ISO-2022-JP 解碼,而緩衝區結束於字元中間時會發生什麼事。

            JIS208-ESC   \x{5f3e}
 A   B   C   ....   ~   \e   $   B  |DAN | ....
41  42  43   ....  7E   1b  24  41  43  46 ....
<- buffer --------------------------->
<< encoded >>>>>>>>>>>>>>>>>>>>>>>

正如您所見,下一個緩衝區以 \x43 開頭。但是 \x43 在 ASCII 中是「C」,這在這種情況下是錯誤的,因為我們現在位於 JISX 0208 區域,因此它必須轉換 \x43\x46,而不是 \x43。與 utf8 和 EUC 不同,在基於跳脫的編碼中,您無法判斷給定的八位元組是整個字元還是只是其中的一部分。

幸運的是,如果您告訴 PerlIO 使用行緩衝區而不是固定緩衝區,PerlIO 也支援行緩衝區。由於 ISO-2022-JP 保證在行尾還原為 ASCII,因此使用行緩衝區時永遠不會發生部分字元。

要告訴 PerlIO 使用行緩衝區,請為編碼物件實作 ->needs_lines 方法。有關詳細資訊,請參閱 Encode::Encoding

感謝這些努力,附帶 Encode 的大多數編碼都支援 PerlIO,但仍留下以下編碼。

iso-2022-kr
MIME-B
MIME-Header
MIME-Q

幸運的是,iso-2022-kr 幾乎沒有使用(根據 Jungshik),而且 MIME-* 非常不可能提供給 PerlIO,因為它們是郵件標頭。有關詳細資訊,請參閱 Encode::MIME::Header

如何判斷我的編碼是否完全支援 PerlIO ?

在撰寫本文時,任何類別屬於 Encode::XS 和 Encode::Unicode 的編碼都可以使用。Encode 模組有一個 perlio_ok 方法,您可以在將 PerlIO 編碼套用至檔案處理之前使用它。以下是一個範例

my $use_perlio = perlio_ok($enc);
my $layer = $use_perlio ? "<:raw" : "<:encoding($enc)";
open my $fh, $layer, $file or die "$file : $!";
while(<$fh>){
  $_ = decode($enc, $_) unless $use_perlio;
  # .... 
}

另請參閱

Encode::EncodingEncode::SupportedEncode::PerlIO編碼perlebcdic"open" in perlfuncperlunicodeutf8、Perl Unicode 郵件清單 <perl-unicode@perl.org>