內容

名稱

編碼 - 允許您使用非 ASCII 和非 UTF-8 編寫腳本

警告

此模組已於 perl v5.18 中棄用。請參閱 "說明""錯誤"

概要

use encoding "greek";  # Perl like Greek to you?
use encoding "euc-jp"; # Jperl!

# or you can even do this if your shell supports your native encoding

perl -Mencoding=latin2 -e'...' # Feeling centrally European?
perl -Mencoding=euc-kr -e'...' # Or Korean?

# more control

# A simple euc-cn => utf-8 converter
use encoding "euc-cn", STDOUT => "utf8";  while(<>){print};

# "no encoding;" supported
no encoding;

# an alternate way, Filter
use encoding "euc-jp", Filter=>1;
# now you can use kanji identifiers -- in euc-jp!

# encode based on the current locale - specialized purposes only;
# fraught with danger!!
use encoding ':locale';

說明

此語法用於啟用 Perl 腳本,以非 ASCII 或非 UTF-8 編碼撰寫。它會將 Perl 程式腳本的全部或部分從特定編碼轉換為 UTF-8,並將 STDINSTDOUT 的 PerlIO 層變更為指定的編碼。

此實用範例源自於 UTF-8 編輯器不常見的時代。但那已經是好久以前的事了,對它的需求也大幅減少。這一點,加上它無法與執行緒搭配運作,以及其他問題(請參閱 "BUGS"),已導致它被棄用。計畫在未來的 Perl 版本中移除此實用範例。新的程式碼應以 UTF-8 編寫,並改用 use utf8 實用範例(有關詳細資訊,請參閱 perluniintroutf8)。舊程式碼應轉換為 UTF-8,方法類似於 "SYNOPSIS" 中的範例(不過此簡單方法可能需要在事後進行手動調整)。

如果 UTF-8 不是選項,建議使用簡單的來源篩選器,例如 CPAN 上的 Filter::Encoding 所提供的篩選器,或此實用範例本身的 Filter 選項(請參閱下方)。

此實用範例唯一合法的使用方式幾乎肯定是在每個檔案中只使用一次,置於檔案頂端,並具有檔案範圍,因為檔案可能只會以一種編碼方式撰寫。在 v5.22 之前的 Perl 中,還有其他限制(請參閱 "Prior to Perl v5.22")。

有兩種基本操作模式(加上關閉)

use encoding ['ENCNAME'] ;

請注意:此操作模式自 Perl v5.26 起不再支援。

這是正常操作。它會將 Perl 來源檔案中遇到的各種文字常數從編碼 ENCNAME 轉譯為 UTF-8,並以類似的方式轉換字元代碼點。這會在腳本是 ASCII(用於變數名稱和標點符號等)的組合,但文字資料使用指定編碼時使用。

ENCNAME 是選用的。如果省略,則會使用環境變數 PERL_ENCODING 中指定的編碼。如果未設定此編碼,或 Encode 不知道解析後的編碼,則會擲回錯誤 Unknown encoding 'ENCNAME'

從 Perl v5.8.6(Encode 版本 2.0.1)開始,ENCNAME 可以是名稱 :locale。這是針對非常特殊的應用程式,並記載於下方的 ":locale 子實用範例" 中。

轉換的字面值為 q//, qq//, qr//, qw///, qx//,從 v5.8.1 開始,還有 tr///。執行轉換的運算包括 chrordutf8::upgrade(但不是 utf8::downgrade)和 chomp

同樣從 v5.8.1 開始,DATA 偽檔案句柄會從編碼轉換成 UTF-8。

例如,您可以使用 EUC-JP 編寫程式碼,如下所示

my $Rakuda = "\xF1\xD1\xF1\xCC"; # Camel in Kanji
             #<-char-><-char->   # 4 octets
s/\bCamel\b/$Rakuda/;

如果啟用 use encoding "euc-jp",這與 UTF-8 中的程式碼相同

my $Rakuda = "\x{99F1}\x{99DD}"; # two Unicode Characters
s/\bCamel\b/$Rakuda/;

請參閱下方的 "範例" 以取得更完整的範例。

除非存在且非零的 ${^UNICODE}(從 v5.8.2 開始提供),否則 STDINSTDOUT 的 PerlIO 層會設定為 ":encoding(ENCNAME)"。因此,

use encoding "euc-jp";
my $message = "Camel is the symbol of perl.\n";
my $Rakuda = "\xF1\xD1\xF1\xCC"; # Camel in Kanji
$message =~ s/\bCamel\b/$Rakuda/;
print $message;

會列印

"\xF1\xD1\xF1\xCC is the symbol of perl.\n"

而不是

"\x{99F1}\x{99DD} is the symbol of perl.\n"

您可以提供額外參數來覆寫此設定;請參閱下方。

請注意,STDERR 無論如何都不會變更。

另請注意,非 STD 檔案句柄不會受到影響。請使用 use openbinmode 來變更這些句柄的層。

use encoding ENCNAME, Filter=>1;

此設定的運作方式與上述相同,但 Filter 參數的值為非零會導致整個指令碼(而非僅字面值)從編碼轉換成 UTF-8。這允許來源中的識別碼也使用該編碼。(如果編碼不是 ASCII 的超集,可能會發生問題;想像所有分號都轉換成不同的東西。)可以使用此格式來執行

${"\x{4eba}"}++

(這等於 $human++,其中 human 是單一漢字)。

這實際上表示您的來源程式碼的行為就像是在啟用 'use utf8' 的情況下以 UTF-8 編寫。因此,即使您的編輯器僅支援 Shift_JIS,例如,您仍然可以在《Perl 程式設計,第 3 版》的第 15 章中嘗試範例。

此選項明顯比其他選項慢。

no encoding;

取消設定指令碼編碼。STDINSTDOUT 的層會重設為 ":raw"(未處理的預設原始位元組串流)。

選項

個別設定 STDIN 和/或 STDOUT

STDINSTDOUT 的編碼可以透過 pragma 的參數個別設定

use encoding 'euc-tw', STDIN => 'greek'  ...;

在此情況下,您不能省略第一個 ENCNAMESTDIN => undef 會完全關閉該檔案句柄的 I/O 轉碼。

${^UNICODE}(從 v5.8.2 開始提供)存在且非零時,這些選項將會被完全忽略。有關詳細資訊,請參閱 "${^UNICODE}" in perlvar"-C" in perlrun

:locale 子語法

從 v5.8.6 開始,編碼名稱可以是 :locale。這表示編碼會從目前的區域設定中取得,而不是由語法強制設定。由於一個腳本實際上只能使用一種編碼,因此這個選項很危險。只有當腳本本身是用 ASCII 編寫,且腳本執行時會使用的所有區域設定都是 ASCII 的超集時,這個選項才有意義。這表示腳本本身不會變更,但 I/O 處理會新增指定的編碼,而 chrord 等運算會使用該編碼。

找出 :locale 使用哪個區域設定的邏輯如下

  1. 如果平台支援 langinfo(CODESET) 介面,則傳回的字元集會用作 open 語法的預設編碼。

  2. 如果 1. 無法運作,但我們在 locale 語法下,則環境變數 LC_ALLLANG(依此順序)會用於比對編碼(如果有「.」之後的部分),如果找到任何編碼,則會將其用作 open 語法的預設編碼。

  3. 如果 1. 和 2. 都無法運作,則環境變數 LC_ALLLANG(依此順序)會用於比對任何看起來像 UTF-8 的內容,如果找到任何內容,則會將 :utf8 用作 open 語法的預設編碼。

如果您的區域設定環境變數(LC_ALLLC_CTYPELANG)包含字串「UTF-8」或「UTF8」(不分大小寫),則 STDINSTDOUTSTDERR 的預設編碼,以及任何後續開啟的檔案,都是 UTF-8。

注意事項

副作用

請勿混合多種編碼

請注意,只有僅具有舊式碼點的字面值(字串或正規表示式)會受到影響:如果您混合此類資料

\x{100}\xDF
\xDF\x{100}

資料假設為(Latin 1 和)Unicode,而非您的原生編碼。換句話說,這將在「希臘文」中相符

"\xDF" =~ /\x{3af}/

但這不會

"\xDF\x{100}" =~ /\x{3af}\x{100}/

因為左邊的 \xDF(ISO 8859-7 希臘小寫字母帶重音的 IOTA)不會升級到 \x{3af}(Unicode 希臘小寫字母帶重音的 IOTA),因為左邊有 \x{100}。您不應在同一個字串中混合舊式資料和 Unicode。

此實用工具也會影響 0x80..0xFF 碼點範圍的編碼:通常該範圍內的字元會保留為八位元組位元組(除非它們與碼點為 0x100 或更大的字元結合,在這種情況下,所有字元都需要編碼為 UTF-8),但如果存在 encoding 實用工具,即使 0x80..0xFF 範圍也總是會編碼為 UTF-8。

畢竟,此實用工具最棒的一點是您不必使用 \x{....} 就能用原生編碼拼寫您的名字。因此,請隨時將您的字串放入您的編碼中,並在引號和正規表示式中使用。

在 Perl v5.22 之前

實用工具是每個腳本,而非每個區塊詞彙。只有最後一個 use encodingno encoding 才重要,而且它會影響整個腳本。但是,支援 no encoding 實用工具,而且 use encoding 可以出現在給定腳本中任意多次(儘管只有最後一個有效)。

由於範圍不是詞彙,因此其他模組使用 chrord 等會受到影響。這會導致難以偵錯的詭異、不正確的遠距離動作。

這表示您必須非常小心載入順序

# called module
package Module_IN_BAR;
use encoding "bar";
# stuff in "bar" encoding here
1;

# caller script
use encoding "foo"
use Module_IN_BAR;
# surprise! use encoding "bar" is in effect.

避免這種怪異現象的最佳方法是在載入其他模組後立即使用此實用工具。即

use Module_IN_BAR;
use encoding "foo";

在 Encode 版本 1.87 之前

在 Perl v5.8.1 之前

「NON-EUC」雙位元組編碼

由於 Perl 需要在套用此 pragma 之前剖析指令碼,因此包含第二個位元組中可能含有 '\' (反斜線;\x5c) 的編碼,例如 Shift_JIS 和 Big-5,會失敗,因為第二個位元組可能會意外地跳脫後面的引號字元。

tr///

編碼 pragma 的運作方式是解碼 q//,qq//,qr//,qw///, qx// 等字串文字。在 Perl v5.8.0 中,這不適用於 tr///。因此,

use encoding 'euc-jp';
#....
$kana =~ tr/\xA4\xA1-\xA4\xF3/\xA5\xA1-\xA5\xF3/;
#           -------- -------- -------- --------

無法作為

$kana =~ tr/\x{3041}-\x{3093}/\x{30a1}-\x{30f3}/;
上方字元的圖例
utf8     euc-jp   charnames::viacode()
-----------------------------------------
\x{3041} \xA4\xA1 HIRAGANA LETTER SMALL A
\x{3093} \xA4\xF3 HIRAGANA LETTER N
\x{30a1} \xA5\xA1 KATAKANA LETTER SMALL A
\x{30f3} \xA5\xF3 KATAKANA LETTER N

此反直覺的行為已在 Perl v5.8.1 中修正。

在 Perl v5.8.0 中,您可以透過下列方式解決這個問題;

use encoding 'euc-jp';
#  ....
eval qq{ \$kana =~ tr/\xA4\xA1-\xA4\xF3/\xA5\xA1-\xA5\xF3/ };

請注意 tr// 運算式被 qq{} 包圍。這個概念與讓 tr///「內插」的經典慣用語相同

tr/$from/$to/;            # wrong!
eval qq{ tr/$from/$to/ }; # workaround.

範例 - Greekperl

use encoding "iso 8859-7";

# \xDF in ISO 8859-7 (Greek) is \x{3af} in Unicode.

$a = "\xDF";
$b = "\x{100}";

printf "%#x\n", ord($a); # will print 0x3af, not 0xdf

$c = $a . $b;

# $c will be "\x{3af}\x{100}", not "\x{df}\x{100}".

# chr() is affected, and ...

print "mega\n"  if ord(chr(0xdf)) == 0x3af;

# ... ord() is affected by the encoding pragma ...

print "tera\n" if ord(pack("C", 0xdf)) == 0x3af;

# ... as are eq and cmp ...

print "peta\n" if "\x{3af}" eq  pack("C", 0xdf);
print "exa\n"  if "\x{3af}" cmp pack("C", 0xdf) == 0;

# ... but pack/unpack C are not affected, in case you still
# want to go back to your native encoding

print "zetta\n" if unpack("C", (pack("C", 0xdf))) == 0xdf;

錯誤

執行緒安全性

use encoding ... 不是執行緒安全的(即,請勿在執行緒應用程式中使用)。

無法在單一程式中由多個模組使用。

只允許一種編碼。如果您在程式中結合具有不同編碼的模組,只會實際使用一種。

其他使用 STDINSTDOUT 的模組會取得編碼串流

它們可能會預期完全不同的東西。

長度超過 127 位元組的 regex 中的文字

對於原生多位元組編碼(固定或可變長度),正規運算式的目前實作可能會為長度超過 127 位元組的正規運算式文字引入重新編碼錯誤。

EBCDIC

編碼 pragma 不支援 EBCDIC 平台。

format

此 pragma 無法與 format 很好地搭配,因為 PerlIO 與它相處得不太好。當 format 包含非 ASCII 字元時,它會列印出奇怪的東西或取得「寬字元警告」。若要了解它,請嘗試下列程式碼。

# Save this one in utf8
# replace *non-ascii* with a non-ascii string
my $camel;
format STDOUT =
*non-ascii*@>>>>>>>
$camel
.
$camel = "*non-ascii*";
binmode(STDOUT=>':encoding(utf8)'); # bang!
write;              # funny
print $camel, "\n"; # fine

在沒有 binmode 的情況下,這會運作,但在沒有 binmode 的情況下,print() 會失敗,而不是 write()。

無論如何,當涉及到 unicode 字元時,format 的使用非常可疑,因為你必須考慮字元寬度(例如,表意文字的雙寬度)和方向(例如,阿拉伯語和希伯來語的 BIDI)。

另請參閱 "CAVEATS"

歷史

此實用程式首次出現在 Perl v5.8.0 中。它已在後續版本中得到增強,如上所述。

另請參閱

perlunicodeEncodeopenFilter::Util::Call

Larry Wall、Tom Christiansen、Jon Orwant 所著的《程式設計 Perl(第 3 版)》第 15 章;O'Reilly & Associates;ISBN 0-596-00027-8