perllocale - Perl 地區設定處理(國際化和在地化)
最初有 ASCII,「美國資訊交換標準碼」,對於使用英文字母和美元貨幣的美國人來說,運作良好。但對於其他英語使用者來說,運作就不那麼好了,因為他們可能使用不同的貨幣,例如英鎊(因為該貨幣的符號不在 ASCII 中);而且對於世界上數千種其他語言來說,這根本不夠用。
為了解決這些缺點,發明了地區設定的概念(正式名稱為 ISO C、XPG4、POSIX 1.c「地區設定系統」)。而且,應用程式已經而且正在撰寫中,使用地區設定機制。讓此類應用程式考量其使用者在這些事項上的偏好的程序稱為國際化(通常縮寫為i18n);告訴此類應用程式關於特定偏好設定集稱為在地化(l10n)。
Perl 已擴充以支援地區設定系統中可用的特定類型的地區設定。這由一個 pragma、一個函式呼叫和幾個環境變數透過每個應用程式控制。
Perl 支援單位元組地區設定,這些地區設定是 ASCII 的超集,例如 ISO 8859,以及下一個段落中說明的多位元組類型地區設定,UTF-8。Perl 不支援任何其他多位元組地區設定,例如東亞語言的地區設定。
很不幸的是,地區設定的設計(以及通常的實作)有很多缺陷。Unicode 的發明(請參閱 perlunitut 以了解其簡介)部分原因是為了解決這些設計缺陷,而現今有一系列基於 Unicode 的「UTF-8 地區設定」。這些地區設定的字元集是 Unicode,編碼為 UTF-8。從 v5.20 開始,Perl 完全支援 UTF-8 地區設定,但排序和字串比較(例如 lt
和 ge
)除外。從 v5.26 開始,Perl 也能合理地處理這些問題,具體取決於平台的實作。但是,對於較早的版本或為了更好的控制,請使用 Unicode::Collate。實際上有兩種略有不同的 UTF-8 地區設定類型:一種適用於突厥語系,一種適用於其他所有語言。
從 Perl v5.30 開始,Perl 會根據其行為偵測突厥語系地區設定,並無縫處理這兩種類型;之前僅支援非突厥語系地區設定。如果您的系統有 tr_TR.UTF-8
地區設定,但其行為不像突厥語系地區設定,Perl 會將其視為非突厥語系地區設定,而忽略地區設定的名稱。
Perl 也持續支援舊的非 UTF-8 地區設定。目前 EBCDIC 平台沒有 UTF-8 地區設定。
(Unicode 也正在建立「共通地區資料儲存庫」CLDR
,http://cldr.unicode.org/,其中包含的資訊類型比 POSIX 地區設定系統提供的還要多。在撰寫本文時,沒有 CPAN 模組可以存取這個 XML 編碼的資料。但是,可以從中計算出 POSIX 地區設定資料,而較早的 CLDR 版本已經將這些資料萃取為 UTF-8 地區設定 http://unicode.org/Public/cldr/2.0.1/。)
地區設定是一組資料,用來描述世界上不同社群如何分類其世界的各個面向。這些類別可細分為以下類型(其中一些在此附有簡短說明)
LC_NUMERIC
:數字格式化這表示數字應如何格式化以供人類閱讀,例如用作小數點的字元。
LC_MONETARY
:貨幣金額格式LC_TIME
:日期/時間格式LC_MESSAGES
:錯誤和其他訊息LC_COLLATE
:校對這表示用於比較和排序的字母順序。例如,在拉丁字母中,「b」通常在「a」之後。
LC_CTYPE
:字元類型例如,這表示字元是否為大寫字母。
有些平台有其他類別,用於處理測量單位和紙張大小等事項。Perl 沒有直接使用這些類別,但 Perl 互動的外部操作可能會使用這些類別。請參閱下方的 "不在「use locale」範圍內"。
Perl 使用的類別的更多詳細資訊如下方 "LOCALE CATEGORIES" 所述。
這些類別加起來,可以讓單一程式客製化,以便在許多不同的地方執行。但仍有不足之處,因此請繼續閱讀。
除非特別要求,否則 Perl 本身(POSIX 模組除外)不會使用區域設定(但再次注意,Perl 可能與使用區域設定的程式碼互動)。即使有此要求,也必須符合所有下列條件,才能正常運作
您的作業系統必須支援區域設定系統。如果支援,您應該會發現 setlocale()
函式是其 C 函式庫中已記錄的部分。
您使用的區域設定定義必須已安裝。您或您的系統管理員必須確保如此。可用的區域設定、它們的儲存位置以及安裝方式因系統而異。有些系統僅提供少數已固定的區域設定,且不允許新增更多區域設定。其他系統允許您新增系統供應商提供的「罐頭」區域設定。其他系統則允許您或系統管理員定義和新增任意區域設定。(您可能必須要求供應商提供未隨您的作業系統提供的罐頭區域設定。)請閱讀您的系統文件以取得進一步說明。
Perl 必須相信區域系統受支援。如果受支援,perl -V:d_setlocale
會顯示 d_setlocale
的值為 define
。
如果您希望 Perl 應用程式根據特定區域處理和顯示您的資料,應用程式程式碼應包含 use locale
pragma(請參閱 "The "use locale" pragma"),並至少符合下列其中一項條件:
決定區域的環境變數(請參閱 "ENVIRONMENT")必須在應用程式啟動時正確設定,由您或設定系統帳戶的人員設定;或
應用程式必須使用 "setlocale 函數" 中說明的方法設定自己的區域。
"use locale"
pragma從 Perl 5.28 開始,此 pragma 可用於具備執行緒安全區域功能的系統上,在 多執行緒 應用程式中。有些注意事項,請參閱下方的 "多執行緒"。在沒有此功能的系統上,或在較早的 Perl 版本中,請勿在有多個 執行緒 處於活動狀態的指令碼中使用此 pragma。在這些情況下,區域並非特定於單一執行緒。另一個執行緒可能會隨時變更區域,這可能會導致特定執行緒至少在它預期之外的區域中執行。在某些平台上,也可能發生區段錯誤。區域變更不必是明確的;某些作業會導致 perl 本身變更區域。只要執行 "use locale"
,您就容易受到攻擊。
預設情況下,Perl 本身(在 POSIX 模組之外)會忽略目前的區域。use locale
pragma 告訴 Perl 對某些作業使用目前的區域。從 v5.16 開始,此 pragma 有以下說明的選用參數,用於限制受其影響的作業。
目前的區域會在執行時間由下述 setlocale() 設定。如果在程式執行過程中尚未呼叫該函數,目前的區域會由程式開始時有效的 "ENVIRONMENT" 決定。如果沒有有效的環境,目前的區域會是系統預設值設定的任何值。在 POSIX 系統上,可能是「C」區域,但並非一定如此。在 Windows 上,預設值會透過電腦的 控制台->區域和語言選項
(或其目前的等效選項)設定。
受區域設定影響的運算有
"use locale"
範圍內只有某些運算(全部來自 Perl 外部)會受到影響,如下所示
此外,Perl 會透過 POSIX 模組存取各種 C 函式庫函式。其中一些函式會永遠受到目前的區域設定影響。例如,POSIX::strftime()
使用 LC_TIME
;POSIX::strtod()
使用 LC_NUMERIC
;POSIX::strcoll()
和 POSIX::strxfrm()
使用 LC_COLLATE
。所有這些函式都會根據目前的底層區域設定運作,即使該區域設定未公開給 Perl 空間。
這也適用於 I18N::Langinfo。
除了 LC_NUMERIC
之外的所有類別的 XS 模組都會取得底層區域設定,因此它們呼叫的任何 C 函式庫函式都會使用該底層區域設定。如需更多說明,請參閱 perlxs 中的「注意事項」。
請注意,所有 C 程式(包括以 C 編寫的 perl 詮釋器)都永遠有一個底層區域設定。除非呼叫 setlocale() 來變更,否則該區域設定為「C」區域設定。當 Perl 啟動時,它會將底層區域設定變更為 "ENVIRONMENT" 指示的區域設定。在使用 POSIX 模組或撰寫 XS 程式碼時,務必記住,即使程式尚未明確變更底層區域設定,它也可能是「C」以外的區域設定。
use locale
的後續影響在 use locale
範圍內設定的某些 Perl 運算,即使在範圍外也會保留該影響。這些運算包括
write() 的輸出格式是由先前的格式宣告決定的(perlfunc 中的「格式」),因此輸出是否受區域設定影響是由 format()
是否在 use locale
的範圍內決定,而不是 write()
是否在範圍內決定。
正規表示式模式可以使用 qr// 編譯,而實際比對會延後到稍後。同樣地,比對行為是由編譯是否在 use locale
的範圍內決定,而不是比對是否在這樣的範圍內進行。
"use locale";
之下以上所有操作
格式宣告 (perlfunc 中的「format」) 以及任何後續的 write()
都會使用 LC_NUMERIC
。
字串化和輸出 會使用 LC_NUMERIC
。這些包括 print()
、printf()
、say()
和 sprintf()
的結果。
比較運算子 (lt
、le
、cmp
、ge
和 gt
) 會使用 LC_COLLATE
。如果未使用明確的比較函數,sort()
也會受到影響,因為它預設會使用 cmp
。
注意: eq
和 ne
不受區域設定影響:它們總是對其純量運算元執行逐字元比較。此外,如果 cmp
發現其運算元根據目前區域設定指定的排序順序相等,它會繼續執行逐字元比較,而且只有當運算元逐字元完全相等時才會傳回 0 (相等)。如果你真的想知道兩個字串(eq
和 cmp
可能認為不同)是否在區域設定的排序方面相等,請參閱 「類別 LC_COLLATE
:排序」 中的討論。
正規表示式和大小寫修改函數 (uc()
、lc()
、ucfirst()
和 lcfirst()
) 會使用 LC_CTYPE
變數 $!
(及其同義詞 $ERRNO
和 $OS_ERROR
)和 $^E
>(及其同義詞 $EXTENDED_OS_ERROR
)在當作字串使用時會使用 LC_MESSAGES
。
預設行為會透過 no locale
實用指令還原,或在到達包含 use locale
的區塊結束時還原。請注意,use locale
呼叫可以巢狀,而且在內層範圍內有效的內容會在內層範圍結束時還原成外層範圍的規則。
使用區域設定資訊的任何操作的字串結果都會被污染(如果你的 perl 支援污染檢查),因為區域設定可能是不可信的。請參閱 「安全性」。
從 Perl v5.16 開始,以非常有限的方式,以及在 v5.22 中更普遍地,你可以透過新增參數到實用指令來限制這個實用指令特定執行個體啟用的類別。例如,
use locale qw(:ctype :numeric);
在其範圍內只啟用受 LC_CTYPE
和 LC_NUMERIC
影響的那些操作(如上所列)的區域設定感知。
可能的類別有::collate
、:ctype
、:messages
、:monetary
、:numeric
、:time
,以及偽類別 :characters
(如下所述)。
因此你可以說
use locale ':messages';
且僅 $!
和 $^E
會感知地區設定。其他一切都不受影響。
由於 Perl 目前不會對 LC_MONETARY
類別執行任何操作,因此指定 :monetary
實際上什麼都不做。有些系統有其他類別,例如 LC_PAPER
,但 Perl 也不會對它們執行任何操作,且無法在這個實用的引數中指定它們。
您也可以輕鬆地說要使用所有類別,但一個除外,例如,
use locale ':!ctype';
use locale ':not_ctype';
這兩個都表示要啟用所有類別的地區設定感知,但 LC_CTYPE
除外。如果 use locale
是否定形式,則只能在其中指定一個類別引數。
在 v5.22 之前,只有帶有引數的實用程式表單可用
use locale ':not_characters';
(且您必須說 not_
;您不能使用驚嘆號 !
形式)。這個偽類別是指定 :collate
和 :ctype
的簡寫。因此,在否定形式中,它幾乎等於說
use locale qw(:messages :monetary :numeric :time);
我們使用術語「幾乎」,因為 :not_characters
也會在其範圍內開啟 use feature 'unicode_strings'
。此表單在 v5.20 及之後版本中較不實用,且在 "Unicode 和 UTF-8" 中有完整說明,但簡而言之,它告訴 Perl 不要使用地區設定定義的字元部分,即 LC_CTYPE
和 LC_COLLATE
類別。它將改用原生字元集(由 Unicode 延伸)。使用這個參數時,您有責任將外部字元集轉換為原生/Unicode 字元集(如果它是越來越流行的 UTF-8 地區設定之一,它已經會是這樣)。有許多方便的方法可以執行此操作,如 "Unicode 和 UTF-8" 中所述。
警告!在 Perl 5.28 之前或在不支援執行緒安全區域設定運算的系統上,請勿在 執行緒 中使用此函式。區域設定會同時在所有其他執行緒中變更,而且如果您的執行緒被作業系統暫停,並啟動另一個執行緒,該執行緒將不會擁有它預期的區域設定。在某些平台上,如果兩個執行緒幾乎同時呼叫此函式,可能會導致競爭條件,進而導致區段錯誤。此警告不適用於非執行緒建置,或存在且非零的 ${^SAFE_LOCALES}
的 Perl;亦即 Perl 5.28 和更新版本是非執行緒或編譯為區域設定執行緒安全的。在 z/OS 系統上,一旦啟動任何執行緒,此函式就會變成空操作。因此,在該系統上,您可以在建立任何執行緒之前設定區域設定,而該區域設定將會是整個程式中有效的區域設定。
否則,您可以使用 POSIX::setlocale()
函式在執行時間任意切換區域設定
# Import locale-handling tool set from POSIX module.
# This example uses: setlocale -- the function call
# LC_CTYPE -- explained below
# (Showing the testing for success/failure of operations is
# omitted in these examples to avoid distracting from the main
# point)
use POSIX qw(locale_h);
use locale;
my $old_locale;
# query and save the old locale
$old_locale = setlocale(LC_CTYPE);
setlocale(LC_CTYPE, "fr_CA.ISO8859-1");
# LC_CTYPE now in locale "French, Canada, codeset ISO 8859-1"
setlocale(LC_CTYPE, "");
# LC_CTYPE now reset to the default defined by the
# LC_ALL/LC_CTYPE/LANG environment variables, or to the system
# default. See below for documentation.
# restore the old locale
setlocale(LC_CTYPE, $old_locale);
setlocale()
的第一個引數提供類別,第二個引數提供區域設定。類別說明您想要在資料處理的哪個方面套用特定區域設定的規則。類別名稱會在 "區域設定類別" 和 "環境" 中說明。區域設定是與特定語言、國家或地區以及編碼集組合對應的客製化資訊集合的名稱。請繼續閱讀以取得區域設定命名的提示:並非所有系統都會像範例中那樣命名區域設定。
如果未提供第二個引數,且類別不是 LC_ALL
,函式會傳回一個字串,指出類別的目前區域設定。您可以在後續呼叫 setlocale()
時使用此值作為第二個引數,但是在某些平台上,字串是不透明的,一般人無法解譯它代表哪個區域設定。
如果未提供第二個引數,且類別是 LC_ALL
,結果會依實作而定。它可能是串接的區域設定名稱(分隔符號也依實作而定)或單一區域設定名稱。請參閱您的 setlocale(3) 手冊頁面以取得詳細資訊。
如果提供了第二個引數,且它對應到有效的區域設定,類別的區域設定會設定為該值,而函式會傳回目前有效的區域設定值。然後您可以在另一個 setlocale()
呼叫中使用此值。(在某些實作中,傳回值有時可能與您提供為第二個引數的值不同,請將其視為您提供的值的別名。)
如範例所示,如果第二個引數是空字串,類別的區域設定會傳回對應環境變數指定的預設值。通常,這會導致傳回 Perl 啟動時有效的預設值:應用程式在啟動後對環境所做的變更可能會被注意到,也可能不會,這取決於您的系統的 C 函式庫。
請注意,當指定的 use locale
形式不包含所有類別時,Perl 會忽略已排除的類別。
如果 setlocale()
因某些原因失敗(例如,嘗試設定系統未知的區域設定),則不會變更該類別的區域設定,且函式會傳回 undef
。
從 Perl 5.28 開始,在執行 POSIX 2008 執行緒安全區域設定作業的系統上編譯的多執行緒 Perl 中,此函式實際上不會呼叫系統 setlocale
。相反地,會使用這些執行緒安全作業來模擬 setlocale
函式,但以執行緒安全的方式進行。
您可以透過使用以下指令重新編譯 Perl,強制始終使用執行緒安全區域設定作業(如果可用)
-Accflags='-DUSE_THREAD_SAFE_LOCALE'
新增到您對 Configure 的呼叫中。
有關類別的更多資訊,請參閱 setlocale(3)。
從 Perl 5.28 開始,在執行 POSIX 2008 或 Windows 特定的執行緒安全區域設定作業的系統上支援多執行緒區域設定作業。許多現代系統(例如各種 Unix 變體和 Darwin)都有此功能。
您可以透過檢視唯讀布林變數 ${^SAFE_LOCALES}
來判斷在您的系統上使用區域設定是否安全。如果 Perl 不是執行緒,或如果它使用執行緒安全區域設定作業,則值為 1。
從 Visual Studio 2005 開始,Windows 支援執行緒安全作業,且在與 POSIX 2008 相容的系統中也支援。有些平台宣稱支援 POSIX 2008,但實作有漏洞,因此編譯為在這些平台上執行的提示檔案會關閉嘗試使用執行緒安全性。${^SAFE_LOCALES}
在這些平台上會為 0。
請注意,撰寫多執行緒應用程式不會移植到缺乏原生執行緒安全區域設定支援的平台。在有此功能的系統上,您會自動為執行緒 Perl 取得此行為,而無需執行任何動作。如果您因為某些原因不想使用此功能(可能是因為您的系統上的 POSIX 2008 支援有漏洞),您可以手動編譯 Perl 以使用舊的非執行緒安全實作,方法是傳遞引數 -Accflags='-DNO_THREAD_SAFE_LOCALE'
給 Configure。除了 Windows 以外,這將在某些情況下繼續使用某些 POSIX 2008 函式。如果這些函式有漏洞,您可以傳遞以下指令給 Configure,作為替代或額外指令:-Accflags='-DNO_POSIX_2008_LOCALE'
。這也會讓程式碼不使用執行緒安全區域設定。${^SAFE_LOCALES}
在關閉執行緒安全作業的系統上會為 0。
通常在非執行緒建置中,會使用傳統的 setlocale()
,而不是執行緒安全區域設定函式。您可以在有這些函式的系統上強制使用這些函式,方法是將 -Accflags='-DUSE_THREAD_SAFE_LOCALE'
新增到 Configure。
初始程式會使用從環境指定的區域設定啟動,如目前在 "ENVIRONMENT" 中所述。所有新建立的執行緒會從 LC_ALL
設定為 "C"
開始。每個執行緒都可以隨時使用 POSIX::setlocale()
來查詢或切換其區域設定,而不會影響任何其他執行緒。所有依賴區域設定的作業都會自動使用其執行緒的區域設定。
這對完全以 Perl 編寫的任何應用程式來說都應該是完全透明的(除了在 「多執行緒」 區段中提供的少數罕見情況)。XS 模組撰寫人員的資訊請參閱 perlxs 中的「Locale-aware XS code」。
對於系統中可用的 locale,請參閱 setlocale(3),以查看它是否會列出可用的 locale 清單(搜尋 另請參閱 區段)。如果失敗,請嘗試以下命令列
locale -a
nlsinfo
ls /usr/lib/nls/loc
ls /usr/lib/locale
ls /usr/lib/nls
ls /usr/share/locale
並查看它們是否列出類似下列的內容
en_US.ISO8859-1 de_DE.ISO8859-1 ru_RU.ISO8859-5
en_US.iso88591 de_DE.iso88591 ru_RU.iso88595
en_US de_DE ru_RU
en de ru
english german russian
english.iso88591 german.iso88591 russian.iso88595
english.roman8 russian.koi8r
遺憾的是,即使 setlocale()
的呼叫介面已標準化,但 locale 的名稱和存放組態的目錄卻尚未標準化。名稱的基本格式為 語言_地區.編碼,但 語言 之後的後綴部分並不總是存在。語言 和 國家 通常來自標準 ISO 3166 和 ISO 639,分別為國家和世界語言的兩個字母縮寫。編碼 部分通常會提到一些 ISO 8859 字元集,即拉丁語系編碼。例如,ISO 8859-1
是所謂的「西歐編碼」,可用於適當地編碼大多數西歐語言。同樣地,甚至連那個標準的名稱都有好幾種寫法。令人遺憾。
特別值得一提的是兩個特殊的 locale:「C」和「POSIX」。目前它們實際上是相同的 locale:主要差別在於前者是由 C 標準定義,後者是由 POSIX 標準定義。它們定義了 預設 locale,在環境中沒有 locale 資訊時,每個程式都會在其中啟動。(如果您願意,可以稱之為 預設 預設 locale。)其語言為(美國)英語,其字元編碼為 ASCII 或很少見的其超集(例如「DEC 多國字元集 (DEC-MCS)」)。警告。某些廠商提供的 C locale 可能實際上與 C 標準所要求的內容不完全相符。因此請小心。
注意:並非所有系統都有「POSIX」locale(並非所有系統都符合 POSIX),因此當您需要明確指定此預設 locale 時,請使用「C」。
您可能會在 Perl 啟動時遇到以下警告訊息
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LC_ALL = "En_US",
LANG = (unset)
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
這表示您的 locale 設定已將 LC_ALL
設定為「En_US」,且 LANG 存在但沒有值。Perl 嘗試相信您,但無法相信。相反地,Perl 放棄並退回到「C」locale,這是預設 locale,應該不論如何都能運作。(在 Windows 上,它會先嘗試退回到系統預設 locale。)這通常表示您的 locale 設定錯誤,它們提到了您的系統從未聽過的 locale,或者系統中的 locale 安裝有問題(例如,某些系統檔案損毀或遺失)。這些問題有快速而暫時的修正方法,也有更徹底而持久的修正方法。
如果您從原始碼建置 Perl,Perl 測試套件檔案 lib/locale.t 可用於測試您系統上的區域設定。將環境變數 PERL_DEBUG_FULL_TEST
設定為 1,將會輸出詳細的結果。例如,在 Linux 上,您可以說
PERL_DEBUG_FULL_TEST=1 ./perl -T -Ilib lib/locale.t > locale.log 2>&1
除了許多其他測試之外,它還會測試系統上找到的每個區域設定,以查看它們是否符合 POSIX 標準。如果有任何錯誤,它會在輸出結尾附近包含一個摘要,說明哪些區域設定通過所有測試,哪些失敗以及原因。
兩個最快的修復方法是讓 Perl 對任何區域設定不一致保持沉默,或在預設區域設定「C」下執行 Perl。
Perl 對區域設定問題的抱怨可以透過將環境變數 PERL_BADLANG
設定為「0」或「」來消音。此方法實際上只是將問題掃到地毯下:即使 Perl 看到有問題,您也告訴 Perl 保持安靜。如果稍後某些依賴區域設定的東西出現異常,請不要感到驚訝。
Perl 可以透過將環境變數 LC_ALL
設定為「C」在「C」區域設定下執行。此方法可能比 PERL_BADLANG
方法更文明,但設定 LC_ALL
(或其他區域設定變數)也可能會影響其他程式,而不仅仅是 Perl。特別是,從 Perl 內部執行的外部程式將看到這些變更。如果您將新設定設為永久(繼續閱讀),您執行的所有程式都會看到這些變更。請參閱 "環境變數" 以取得相關環境變數的完整清單,並參閱 "使用區域設定" 以取得它們在 Perl 中的效果。其他程式中的效果很容易推論。例如,變數 LC_COLLATE
很可能會影響您的 排序 程式(或系統中按字母順序排列「記錄」的程式)。
您可以暫時測試變更這些變數,如果新的設定似乎有幫助,請將這些設定放入您的 shell 啟動檔案中。請參閱您的當地文件以取得確切的詳細資訊。對於類似 Bourne 的 shell(sh、ksh、bash、zsh)
LC_ALL=en_US.ISO8859-1
export LC_ALL
這假設我們使用上述討論的指令看到語言環境「en_US.ISO8859-1」。我們決定嘗試使用它,而不是上述有問題的語言環境「En_US」--以及在 Cshish shell(csh、tcsh)中
setenv LC_ALL en_US.ISO8859-1
或者如果您有「env」應用程式,您可以在(任何 shell 中)執行
env LC_ALL=en_US.ISO8859-1 perl ...
如果您不知道您有哪個 shell,請諮詢您的當地客服中心或等同機構。
較慢但較好的修正方式是您自己修正您自己的環境變數的錯誤設定。整個系統語言環境的錯誤(遺失)設定通常需要您的友善系統管理員協助。
首先,請參閱本文件較早的段落關於「尋找語言環境」。它說明如何找出您的系統實際支援哪些語言環境,更重要的是,已安裝哪些語言環境。在我們的範例錯誤訊息中,影響語言環境的環境變數會以重要性遞減的順序列出(而未設定的變數不重要)。因此,將 LC_ALL 設定為「En_US」一定是錯誤的選擇,如錯誤訊息所示。請先嘗試修正首先列出的語言環境設定。
其次,如果您使用列出的指令看到完全一樣的內容(前綴比對不算,而且通常會區分大小寫)例如「En_US」沒有引號,那麼您應該沒問題,因為您使用的是應該已安裝且在您的系統中可用的語言環境名稱。在這種情況下,請參閱「永久修正您的系統語言環境設定」。
這是當您看到類似以下內容時
perl: warning: Please check that your locale settings:
LC_ALL = "En_US",
LANG = (unset)
are supported and installed on your system.
但無法看到上述指令列出的「En_US」。您可能會看到類似「en_US.ISO8859-1」的內容,但那不一樣。在這種情況下,請嘗試在您可以列出且在某種程度上與您嘗試的內容相符的語言環境下執行。比對語言環境名稱的規則有點模糊,因為這個領域的標準化很薄弱。請再次參閱「尋找語言環境」以了解一般規則。
連絡系統管理員(最好是您自己的),並回報您收到的確切錯誤訊息,並請他們閱讀您現在正在閱讀的相同文件。他們應該能夠檢查系統的語言環境設定是否有問題。「尋找語言環境」章節對於確切的指令和位置有點模糊,因為這些內容沒有那麼標準化。
POSIX::localeconv()
函數可讓您取得由目前的底層 LC_NUMERIC
和 LC_MONETARY
地區設定所指定的與地區設定相關的數字格式化資訊(無論是否在 use locale
範圍內呼叫)。(如果您只想要特定類別的目前地區設定名稱,請使用帶有一個參數的 POSIX::setlocale()
--請參閱 "setlocale 函數"。)
use POSIX qw(locale_h);
# Get a reference to a hash of locale-dependent info
$locale_values = localeconv();
# Output sorted list of the values
for (sort keys %$locale_values) {
printf "%-20s = %s\n", $_, $locale_values->{$_}
}
localeconv()
不帶任何參數,並傳回 對雜湊的參考。此雜湊的鍵是格式化的變數名稱,例如 decimal_point
和 thousands_sep
。值是對應的,嗯,值。請參閱 POSIX 中的「localeconv」,取得列出實作預期提供的類別的較長範例;有些提供更多,有些則提供較少。您不需要明確的 use locale
,因為 localeconv()
始終會觀察目前的區域設定。
以下是簡單的範例程式,它會將其命令列參數改寫為在目前地區設定中正確格式化的整數
use POSIX qw(locale_h);
# Get some of locale's numeric formatting parameters
my ($thousands_sep, $grouping) =
@{localeconv()}{'thousands_sep', 'grouping'};
# Apply defaults if values are missing
$thousands_sep = ',' unless $thousands_sep;
# grouping and mon_grouping are packed lists
# of small integers (characters) telling the
# grouping (thousand_seps and mon_thousand_seps
# being the group dividers) of numbers and
# monetary quantities. The integers' meanings:
# 255 means no more grouping, 0 means repeat
# the previous grouping, 1-254 means use that
# as the current grouping. Grouping goes from
# right to left (low to high digits). In the
# below we cheat slightly by never using anything
# else than the first grouping (whatever that is).
if ($grouping) {
@grouping = unpack("C*", $grouping);
} else {
@grouping = (3);
}
# Format command line params for current locale
for (@ARGV) {
$_ = int; # Chop non-integer part
1 while
s/(\d)(\d{$grouping[0]}($|$thousands_sep))/$1$thousands_sep$2/;
print "$_";
}
print "\n";
請注意,如果平台沒有 LC_NUMERIC
和/或 LC_MONETARY
可用或已啟用,則雜湊的對應元素將會遺失。
查詢與地區設定相關資訊的另一個介面是 I18N::Langinfo::langinfo()
函數。
以下範例將匯入 langinfo()
函數本身和三個常數,以用作 langinfo()
的參數:星期中縮寫的第一天(編號從星期日 = 1 開始)以及兩個常數,分別用於目前地區設定中對是非問題的肯定和否定答案。
use I18N::Langinfo qw(langinfo ABDAY_1 YESSTR NOSTR);
my ($abday_1, $yesstr, $nostr)
= map { langinfo } qw(ABDAY_1 YESSTR NOSTR);
print "$abday_1? [$yesstr/$nostr] ";
換句話說,在「C」(或英文)地區設定中,上述內容可能會印出類似以下內容
Sun? [yes/no]
請參閱 I18N::Langinfo,取得更多資訊。
以下小節說明基本的地區設定類別。除此之外,某些組合類別允許一次操作多個基本類別。請參閱 "環境",以討論這些類別。
LC_COLLATE
:校對:文字比較和排序在包含校對的 use locale
形式的範圍內,Perl 會查看 LC_COLLATE
環境變數,以判斷應用程式對字元校對(排序)的概念。例如,「b」在拉丁字母中跟在「a」之後,但「á」和「å」屬於哪裡?雖然「color」在英文中跟在「chocolate」之後,但在傳統西班牙文中呢?
以下所有校對都有意義,如果您 "use locale"
,您可能會遇到任何一種。
A B C D E a b c d e
A a B b C c D d E e
a A b B c C d D e E
a b c d e A B C D E
以下是程式碼片段,用於說明目前地區設定中有哪些「字詞」字元,依據該地區設定的順序
use locale;
print +(sort grep /\w/, map { chr } 0..255), "\n";
將此與您看到的字元及其順序進行比較,如果您明確指出應忽略區域設定
no locale;
print +(sort grep /\w/, map { chr } 0..255), "\n";
此機器原生排序 (除非 use locale
先前已出現在同一區塊中,否則您會取得此排序) 必須用於排序原始二進位資料,而第一個範例的與區域設定相關的排序則適用於自然語言文字。
如 "USING LOCALES" 中所述,當 use locale
生效時,cmp
會根據目前的排序區域設定進行比較,但對於區域設定表示相等的字串,則會改為逐字元進行比較。如果您不想要此後備,可以使用 POSIX::strcoll()
use POSIX qw(strcoll);
$equal_in_locale =
!strcoll("space and case ignored", "SpaceAndCaseIgnored");
如果排序區域設定指定完全忽略空白字元且會折疊大小寫的字典式排序,則 $equal_in_locale
會為 true。
Perl 使用平台的 C 函式庫排序函式 strcoll()
和 strxfrm()
。這表示您會取得它們提供的結果。在某些平台上,這些函式在 UTF-8 區域設定上運作良好,提供該區域設定中重要程式碼點的合理預設排序。(如果它們運作不佳,問題可能只是區域設定定義有缺陷,因此可以使用更好的定義檔案來修正。Unicode 的定義 (請參閱 "Freely available locale definitions") 提供合理的 UTF-8 區域設定排序定義。)從 Perl v5.26 開始,Perl 使用這些函式的過程變得更順暢。這可能足以滿足您的需求。若要進行更多控制,並確保包含任何程式碼點 (不只是區域設定中重要的程式碼點) 的字串正確排序,建議使用 Unicode::Collate 模組。
在非 UTF-8 區域設定 (因此為單一位元組) 中,超過 0xFF 的程式碼點在技術上無效。但如果存在,從 v5.26 開始,它們會排序到與最高有效程式碼點相同的位置。這通常會產生良好的結果,但如果有效程式碼點在與其他字元形成特定序列時獲得特殊處理 (如區域設定所定義),則排序順序可能會偏斜。當兩個字串排序相同時,程式碼點順序會用作平手決勝負。
如果 Perl 偵測到區域設定校對順序有問題,它會改用非區域設定校對規則。
如果您有一個單一字串,想要檢查它與其他幾個字串的「區域設定相等性」,您可能會認為可以透過將 POSIX::strxfrm()
與 eq
結合使用來獲得一點效率
use POSIX qw(strxfrm);
$xfrm_string = strxfrm("Mixed-case string");
print "locale collation ignores spaces\n"
if $xfrm_string eq strxfrm("Mixed-casestring");
print "locale collation ignores hyphens\n"
if $xfrm_string eq strxfrm("Mixedcase string");
print "locale collation ignores case\n"
if $xfrm_string eq strxfrm("mixed-case string");
strxfrm()
會取得一個字串,並將它對應到一個轉換後的字串,以用於在校對期間與其他轉換後的字串進行逐字元比對。在「幕後」,受區域設定影響的 Perl 比較運算子會對兩個運算元呼叫 strxfrm()
,然後對轉換後的字串進行逐字元比對。透過明確呼叫 strxfrm()
並使用不受區域設定影響的比對,範例試圖儲存一些轉換。但事實上,它並未儲存任何內容:Perl 魔術(請參閱 "Magic Variables" in perlguts)會在第一次在比對中需要時建立一個字串的轉換版本,然後保留此版本以備不時之需。使用 cmp
重新編寫的範例執行速度幾乎一樣快。它也能處理嵌入在字串中的空字元;如果您直接呼叫 strxfrm()
,它會將找到的第一個空字元視為終止符。不要期望它產生的轉換後字串可以在系統之間移植,甚至無法從作業系統的一個版本移植到下一個版本。簡而言之,不要直接呼叫 strxfrm()
:讓 Perl 為您執行此動作。
注意:在這些範例中的一些範例中未顯示 use locale
,因為不需要:strcoll()
和 strxfrm()
是 POSIX 函式,它們使用標準系統提供的 libc
函式,這些函式始終遵循目前的 LC_COLLATE
區域設定。
LC_CTYPE
:字元類型在包含 LC_CTYPE
的 use locale
形式的範圍內,Perl 會遵循 LC_CTYPE
區域設定。這會控制應用程式對哪些字元為字母、數字、標點符號等的概念。這會影響 Perl 的 \w
正規表示式元標記,它代表字母數字字元,也就是字母、數字和平台的原生底線。(請參閱 perlre 以取得有關正規表示式的更多資訊。)感謝 LC_CTYPE
,根據您的區域設定,像「æ」、「ð」、「ß」和「ø」這樣的字元可能會被理解為 \w
字元。它也會影響 \s
、\D
和 POSIX 字元類別,例如 [[:graph:]]
。(請參閱 perlrecharclass 以取得有關所有這些的更多資訊。)
LC_CTYPE
區域設定也提供用於在小寫和字元之間轉換字元的對應。這會影響大小寫對應函式--fc()
、lc()
、lcfirst()
、uc()
和 ucfirst()
;使用雙引號字串和 s///
替換中的 \F
、\l
、\L
、\u
或 \U
進行大小寫對應內插;以及使用 i
修飾詞進行不區分大小寫的正規表示式模式比對。
從 v5.20 開始,Perl 支援 LC_CTYPE
的 UTF-8 區域設定,但除此之外,Perl 只支援單位元組區域設定,例如 ISO 8859 系列。這表示不支援寬字元區域設定,例如亞洲語言。使用這些區域設定可能會導致核心傾印。如果平台有能力讓 Perl 偵測到此類區域設定,從 Perl v5.22 開始,Perl 會發出警告,預設啟用,使用 locale
警告類別,每當切換到此類區域設定時。UTF-8 區域設定支援實際上是 POSIX 區域設定的超集,因為它實際上是完整的 Unicode 行為,就好像根本沒有 LC_CTYPE
區域設定生效一樣(除了污染;請參閱 "安全性")。POSIX 區域設定,即使是 UTF-8 區域設定,也缺少 Unicode 中的某些概念,例如變更字元大小寫可能會擴充為多個字元。Perl 在 UTF-8 區域設定中會提供該擴充。在 v5.20 之前,Perl 在某些平台上將 UTF-8 區域設定視為 ISO 8859-1 區域設定,並有一些限制,而在其他平台上則更像是「C」區域設定。對於 v5.16 和 v5.18 版本,use locale 'not_characters
可以用作解決方法(請參閱 "Unicode 和 UTF-8")。
請注意,有許多事情不受當前區域設定影響。任何文字字元都是給定平台的原生字元。因此,在 ASCII 平台上「A」表示代碼點 65 處的字元,在 EBCDIC 上表示 193。這在當前區域設定中可能是或可能不是「A」,如果該區域設定甚至有「A」。類似地,所有特定字元的跳脫序列,例如 \n
,總是表示平台的原生字元。這表示,例如,正規表示式中的 \N
(每個字元,但換行符號除外)在平台字元集上運作。
從 v5.22 開始,Perl 預設會在切換到重新定義任何 ASCII 可列印字元(加上 \t
和 \n
)為預期之外不同類別的ロケ設定時發出警告。這很可能會只發生在 EBCDIC 平台上的現代ロケ設定,例如,在 CCSID 1047 電腦上的 CCSID 0037 會移動 "["
,但它也可能發生在具有 ISO 646 和其他基本上已過時的 7 位元組ロケ設定的 ASCII 平台上。這可能會運作,具體取決於程式使用的 Perl 功能。例如,在上述範例中,"|"
會變成 \w
,而且沒有正規表示式會受到影響,程式可能仍會正常運作。警告會列出所有可能受到負面影響的字元。
注意:有問題或惡意的 LC_CTYPE
區域設定定義可能會導致應用程式將明顯不合格的字元視為字母數字。對於(普通)ASCII 字母和數字的嚴格比對,例如在命令字串中,使用區域設定的應用程式應將 \w
與正規表示式修改符號 /a
搭配使用。請參閱 "安全性"。
LC_NUMERIC
:數字格式化在正確呼叫 POSIX::setlocale()
之後,且在包含數字的 use locale
形式的範圍內,Perl 會遵守 LC_NUMERIC
區域設定資訊,這會控制應用程式如何將數字格式化為人類可讀的格式。在大部分實作中,唯一的效果是變更用於小數點的字元,例如從「.」變更為「,」。這些函式不知道像千位分隔符號等細節。(如果您關心這些事項,請參閱 "localeconv 函式"。)
use POSIX qw(strtod setlocale LC_NUMERIC);
use locale;
setlocale LC_NUMERIC, "";
$n = 5/2; # Assign numeric 2.5 to $n
$a = " $n"; # Locale-dependent conversion to string
print "half five is $n\n"; # Locale-dependent output
printf "half five is %g\n", $n; # Locale-dependent output
print "DECIMAL POINT IS COMMA\n"
if $n == (strtod("2,5"))[0]; # Locale-dependent conversion
另請參閱 I18N::Langinfo 和 RADIXCHAR
。
LC_MONETARY
:貨幣金額格式化C 標準定義了 LC_MONETARY
類別,但沒有受到其內容影響的函式。(有標準委員會經驗的人會知道工作小組決定暫緩處理此問題。)因此,Perl 基本上不理會它。如果您真的想使用 LC_MONETARY
,您可以查詢其內容(請參閱 "localeconv 函式"),並在應用程式自己的貨幣金額格式化中使用它所傳回的資訊。不過,您可能會發現這些資訊雖然龐大且複雜,但仍然無法完全滿足您的需求:貨幣格式化是一個難以解決的問題。
另請參閱 I18N::Langinfo 和 CRNCYSTR
。
LC_TIME
:時間表示由 POSIX::strftime()
產生的輸出,會建立一個格式化的人類可讀日期/時間字串,會受到目前的 LC_TIME
地區設定影響。因此,在法語地區設定中,%B
格式元素(完整月份名稱)所產生的輸出,對於今年的第一個月將會是「janvier」。以下是取得目前地區設定中長月份名稱清單的方法
use POSIX qw(strftime);
for (0..11) {
$long_month_name[$_] =
strftime("%B", 0, 0, 0, 1, $_, 96);
}
注意:此範例中不需要 use locale
:strftime()
是一個 POSIX 函式,它使用標準系統提供的 libc
函式,該函式總是遵循目前的 LC_TIME
地區設定。
另請參閱 I18N::Langinfo 和 ABDAY_1
..ABDAY_7
、DAY_1
..DAY_7
、ABMON_1
..ABMON_12
和 ABMON_1
..ABMON_12
。
其餘的地區設定類別目前並未由 Perl 本身使用。但請再次注意,Perl 互動的項目可能會使用這些類別,包括標準 Perl 發行版外部的擴充功能,以及作業系統及其公用程式。特別注意,$!
的字串值和外部公用程式給出的錯誤訊息可能會因 LC_MESSAGES
而有所變更。如果您想要有可攜式的錯誤碼,請使用 %!
。請參閱 Errno。
雖然有關 Perl 安全性問題的主要討論可以在 perlsec 中找到,但如果沒有讓您注意與地區設定相關的安全問題,那麼對 Perl 地區設定處理的討論將是不完整的。地區設定(特別是在允許非特權使用者建立自己的地區設定的系統上)是不可信賴的。惡意的(或只是完全損壞的)地區設定可能會讓地區設定感知應用程式產生意外的結果。以下列出一些可能性
使用 \w
進行安全檔案名稱或電子郵件地址的正規表示式檢查,可能會受到 LC_CTYPE
地區設定的欺騙,該地區設定宣稱 ">"
和 "|"
等字元為字母數字。
字串內插與大小寫對應,例如 $dest = "C:\U$name.$ext"
,如果一個虛假的 LC_CTYPE
大小寫對應表格生效,可能會產生危險的結果。
一個狡猾的 LC_COLLATE
區域設定可能會導致成績為「D」的學生的姓名出現在成績為「A」的學生之前。
如果區域設定遭到破壞,一個費心使用 LC_MONETARY
中資訊的應用程式可能會將借項格式化為貸項,反之亦然。或者,它可能會以美元而不是港幣進行付款。
能夠破壞 LC_DATE
區域設定的惡意使用者可以利用 strftime()
格式化的日期和星期名稱來獲取優勢。(「看——它說我星期日不在大樓裡。」)
這些危險並非區域設定系統特有:任何可能遭到惡意修改的應用程式環境方面都會出現類似的挑戰。同樣地,它們並非 Perl 特有:任何允許您撰寫會考量其環境的程式的程式語言都會讓您面臨這些問題。
Perl 無法保護您免於範例中顯示的所有可能性——沒有任何東西可以取代您自己的警覺性——但當 use locale
生效時,Perl 會使用污染機制(請參閱 perlsec)來標記會變得依賴於區域設定且因此可能不可信的字串結果。
請注意,可以編譯沒有污染支援的 Perl,在這種情況下,所有污染功能都會靜默地不執行任何動作。
以下是可能會受到區域設定影響的運算子與函式的污染行為摘要
比較運算子(lt
、le
、ge
、gt
和 cmp
)
純量 true/false(或小於/等於/大於)結果永遠不會被污染。
大小寫轉換內插(使用 \l
、\L
、\u
、\U
或 \F
)
如果包含 LC_CTYPE
的 use locale
形式生效,則包含內插資料的結果字串會被污染。
匹配運算子(m//
)
純量 true/false 結果永遠不會被污染。
如果包含 LC_CTYPE
的 use locale
形式生效,並且子模式正規表示式包含依賴於區域設定的建構,則所有子模式(以清單內容結果或 $1
等形式傳遞)都會被污染。這些建構包括 \w
(用於匹配字母數字字元)、\W
(非字母數字字元)、\b
和 \B
(字首和非字首,取決於 \w
和 \W
匹配的內容)、\s
(空白字元)、\S
(非空白字元)、\d
和 \D
(數字和非數字),以及 POSIX 字元類別,例如 [:alpha:]
(請參閱 "POSIX Character Classes" in perlrecharclass)。
如果模式要進行不分大小寫的比對(透過 /i
),也有可能發生污染。例外情況是,所有要以這種方式比對的碼點都大於 255,且根據 Unicode 規則,沒有折疊到 256 以下。Perl 僅對此類碼點使用 Unicode 規則,而這些規則不論目前區域設定為何,都是相同的,因此不會對這些碼點進行污染。
比對模式變數 $&
、$`
(比對前)、$'
(比對後)和 $+
(最後比對)也會受到污染。
取代運算子 (s///
)
行為與比對運算子相同。此外,如果 use locale
表單包含 LC_CTYPE
,且因為涉及前一項目中所述事項的正規表示式比對或大小寫轉換(例如 \l
、\L
、\u
、\U
或 \F
)而進行修改,則 =~
的左運算元會受到污染。
輸出格式化函數 (printf()
和 write()
)
結果永遠不會受到污染,因為否則即使是列印的輸出(例如 print(1/7)
)也應該會在啟用污染檢查時受到污染。
大小寫轉換函數 (lc()
、lcfirst()
、uc()
、ucfirst()
)
如果 use locale
表單包含 LC_CTYPE
,則結果會受到污染。
POSIX 區域設定相關函數 (localeconv()
、strcoll()
、strftime()
、strxfrm()
)
結果永遠不會受到污染。
三個範例說明了區域設定相關的污染。第一個程式忽略其區域設定,不會執行:如果啟用了污染檢查,則無法使用直接從命令列取得的值來命名輸出檔案。
#/usr/local/bin/perl -T
# Run with taint checking
# Command line sanity check omitted...
$tainted_output_file = shift;
open(F, ">$tainted_output_file")
or warn "Open of $tainted_output_file failed: $!\n";
可以透過正規表示式「洗滌」受污染的值來讓程式執行:第二個範例(仍忽略區域設定資訊)會執行,並在可能的情況下建立其命令列上指定的檔案。
#/usr/local/bin/perl -T
$tainted_output_file = shift;
$tainted_output_file =~ m%[\w/]+%;
$untainted_output_file = $&;
open(F, ">$untainted_output_file")
or warn "Open of $untainted_output_file failed: $!\n";
將此與類似但具有區域設定感知的程式進行比較
#/usr/local/bin/perl -T
$tainted_output_file = shift;
use locale;
$tainted_output_file =~ m%[\w/]+%;
$localized_output_file = $&;
open(F, ">$localized_output_file")
or warn "Open of $localized_output_file failed: $!\n";
這個第三個程式無法執行,因為 $&
受到污染:它是包含 \w
的比對結果,且在 use locale
啟用時發生。
此環境變數在 Perl v5.20 開始提供,如果設定(為任何值),會指示 Perl 不要使用其他環境變數來初始化。相反地,Perl 會使用目前的區域設定。這在嵌入式環境中特別有用,請參閱 "在 perlembed 中使用嵌入式 Perl 與 POSIX 區域設定"。
一個字串,可用於抑制 Perl 在啟動時發出有關區域設定失敗的警告。如果作業系統中的區域設定支援在某些方面有缺陷(損壞),或者您在設定環境時輸入區域設定名稱時輸入錯誤,就可能會發生失敗。如果這個環境變數不存在,或其值不是「0」或「」,Perl 會抱怨區域設定失敗。
注意:PERL_BADLANG
僅提供一種方式讓您隱藏警告訊息。該訊息說明系統的區域設定支援中存在一些問題,您應該調查問題出在哪裡。
下列環境變數並非 Perl 專屬:它們是標準化(ISO C、XPG4、POSIX 1.c)setlocale()
方法的一部分,用於控制應用程式對資料的看法。Windows 並非 POSIX,但 Perl 仍會安排下列變數照說明運作。如果環境變數指定的區域設定無效,Perl 會嘗試優先使用下一個較低的區域設定。如果沒有任何一個有效,Windows 會嘗試使用系統預設的區域設定。如果所有方法都失敗,就會使用 "C"
區域設定。如果連這個方法都不行,代表某個地方出了大問題,但 Perl 會嘗試使用任何可能的區域設定繼續執行。
LC_ALL
LC_ALL
是「覆寫所有」的區域設定環境變數。如果已設定,它會覆寫所有其他區域設定環境變數。
LANGUAGE
注意:LANGUAGE
是 GNU 延伸功能,只有在您使用 GNU libc 時才會影響您。例如,如果您使用 Linux,就會是這種情況。如果您使用「商業」Unix,您很可能沒有使用 GNU libc,而且可以忽略 LANGUAGE
。
不過,如果您使用 LANGUAGE
:它會影響指令輸出之資訊、警告和錯誤訊息的語言(換句話說,它就像 LC_MESSAGES
),但它的優先權高於 LC_ALL
。此外,它不是單一值,而是一個由 語言(而非區域設定)組成的「路徑」(以「:」分隔的清單)。請參閱 GNU gettext
函式庫文件,以取得更多資訊。
LC_CTYPE
在沒有 LC_ALL
的情況下,LC_CTYPE
會選擇字元類型區域設定。在沒有 LC_ALL
和 LC_CTYPE
的情況下,LANG
會選擇字元類型區域設定。
LC_COLLATE
在沒有 LC_ALL
的情況下,LC_COLLATE
會選擇排序(整理)區域設定。在沒有 LC_ALL
和 LC_COLLATE
的情況下,LANG
會選擇排序區域設定。
LC_MONETARY
在沒有 LC_ALL
的情況下,LC_MONETARY
會選擇貨幣格式區域設定。在沒有 LC_ALL
和 LC_MONETARY
的情況下,LANG
會選擇貨幣格式區域設定。
LC_NUMERIC
在沒有 LC_ALL
的情況下,LC_NUMERIC
會選擇數字格式區域設定。在沒有 LC_ALL
和 LC_NUMERIC
的情況下,LANG
會選擇數字格式。
LC_TIME
在沒有 LC_ALL
的情況下,LC_TIME
會選擇日期和時間格式區域設定。在沒有 LC_ALL
和 LC_TIME
的情況下,LANG
會選擇日期和時間格式區域設定。
LANG
LANG
是「萬用」地區環境變數。如果已設定,它會在整體 LC_ALL
和類別特定的 LC_foo
之後作為最後手段使用。
LC_NUMERIC
控制數字輸出
use locale;
use POSIX qw(locale_h); # Imports setlocale() and the LC_ constants.
setlocale(LC_NUMERIC, "fr_FR") or die "Pardon";
printf "%g\n", 1.23; # If the "fr_FR" succeeded, probably shows 1,23.
以及 POSIX::strtod()
如何將字串剖析為數字
use locale;
use POSIX qw(locale_h strtod);
setlocale(LC_NUMERIC, "de_DE") or die "Entschuldigung";
my $x = strtod("2,34") + 5;
print $x, "\n"; # Probably shows 7,34.
eval
和 LC_NUMERIC
字串 eval 會將其表達式剖析為標準 Perl。因此,它預期小數點為句點。如果將 LC_NUMERIC
設定為改用逗號,剖析會混淆,甚至可能在無聲中進行。
use locale;
use POSIX qw(locale_h);
setlocale(LC_NUMERIC, "fr_FR") or die "Pardon";
my $a = 1.2;
print eval "$a + 1.5";
print "\n";
會印出 13,5
。這是因為在該地區環境中,逗號是小數點字元。因此,eval
會擴充為
eval "1,2 + 1.5"
結果並非您預期的。不會產生任何警告。如果您在 use locale
範圍內執行字串 eval
,您應該將 eval
行變更為類似下列內容
print eval "no locale; $a + 1.5";
這會印出 2.7
。
您也可以排除 LC_NUMERIC
(如果您不需要),方法如下
use locale ':!numeric';
5.004 之前的 Perl 版本大多數會忽略地區環境資訊,通常表現得好像總是強制執行類似 "C"
地區環境,即使程式環境另有建議(請參閱 "setlocale 函式")。預設情況下,Perl 仍會以這種方式執行,以維持向後相容性。如果您希望 Perl 應用程式注意地區環境資訊,您必須使用 use locale
實用指令(請參閱 "「use locale」實用指令"),或者在不太可能的情況下,您只想對樣式比對執行此操作,使用 /l
正規表示式修飾詞(請參閱 "perlre 中的字元集修飾詞")指示它執行此操作。
5.002 至 5.003 版本的 Perl 會使用 LC_CTYPE
資訊(如果可用);也就是說,\w
會根據地區環境變數了解哪些是字母。問題在於使用者無法控制此功能:如果 C 函式庫支援地區環境,Perl 就會使用它們。
在 Perl 5.004 之前的版本中,可以使用 I18N::Collate
函式庫模組進行區域性校對。這個模組現在已經有點過時了,在新的應用程式中應該避免使用。LC_COLLATE
功能現在已經整合到 Perl 核心語言中:你可以使用 use locale
完全正常地使用特定區域性的標量資料,因此不再需要使用 I18N::Collate
的標量參照來處理了。
根據區域性進行比較和排序通常比預設排序慢;已經觀察到速度會變慢兩到四倍。它還會消耗更多記憶體:一旦 Perl 標量變數參與任何遵循區域性校對規則的字串比較或排序操作,它將比以前佔用多 3-15 倍的記憶體。(確切的倍數取決於字串內容、作業系統和區域性。)這些缺點更多是由作業系統的區域性系統實作所決定的,而不是由 Perl 所決定的。
Unicode CLDR 專案會擷取許多區域性的 POSIX 部分,可在以下位置取得:
https://unicode.org/Public/cldr/2.0.1/
(較新版本的 CLDR 需要你自己計算 POSIX 資料。請參閱 http://unicode.org/Public/cldr/latest/。)
在以下位置有一個大型的區域性定義集合:
http://std.dkuug.dk/i18n/WG15-collection/locales/
你應該知道它不受支援,而且不聲稱適合任何用途。如果你的系統允許安裝任意區域性,你可能會發現這些定義很有用,或者可以作為開發你自己的區域性的基礎。
「國際化」通常縮寫為 i18n,因為它的第一個和最後一個字母之間隔著另外十八個字母。(你可以猜猜為什麼 internalin ... internaliti ... i18n 傾向於被縮寫。)同樣地,「在地化」通常縮寫為 l10n。
國際化,如 C 和 POSIX 標準中所定義的,可以被批評為不完整且笨拙。它們還傾向於像標準組織一樣,將世界劃分為國家,而我們都知道,世界也可以很好地劃分為銀行家、騎士、遊戲玩家等等。
Unicode 的支援是從 Perl 版本 v5.6 開始,且在 v5.8 和後續版本中更為完整地實作。請參閱 perluniintro。
從 Perl v5.20 開始,Perl 支援 UTF-8 地區設定,但 LC_COLLATE
僅部分支援;Perl v5.26 中的校對支援已改善至可能足夠滿足您的需求的程度(請參閱 "類別 LC_COLLATE
:校對:文字比較和排序")。
如果您有 Perl v5.16 或 v5.18 且無法升級,您可以使用
use locale ':not_characters';
當使用此形式的 pragma 時,Perl 僅使用地區設定的非字元部分,例如 LC_NUMERIC
。Perl 假設您已將所有要操作的字元翻譯成 Unicode(實際上是平台的原生字元集(ASCII 或 EBCDIC)加上 Unicode)。對於檔案中的資料,也可以透過同時指定來方便地執行此操作
use open ':locale';
此 pragma 安排將所有來自檔案的輸入從環境中指定的目前地區設定翻譯成 Unicode(請參閱 "ENVIRONMENT"),並將所有輸出翻譯回地區設定。(請參閱 open)。在每個檔案處理的基礎上,您可以改用 PerlIO::locale 模組或 Encode::Locale 模組,這兩個模組都可以在 CPAN 取得。後者模組也有方法可以簡化 ARGV
和環境變數的處理,且可用於個別字串。如果您知道您的所有地區設定都會是 UTF-8(就像現在許多地區設定一樣),您可以使用 -C 命令列開關。
此形式的 pragma 允許實質上無縫地處理具有 Unicode 的地區設定。校對順序將依 Unicode 碼點順序。可以透過 Unicode::Collate 取得 Unicode 規則校對。
剛才說明的所有模組和開關都可以在 v5.20 中使用單純的 use locale
,且如果輸入地區設定不是 UTF-8,您會得到比理想情況更差的行為,如下所述,這會發生在 v5.16 之前的 Perl,或在 v5.16 和 v5.18 中使用 locale pragma 但沒有 :not_characters
參數時。如果您在 v5.20 和更高版本中只使用 UTF-8 地區設定,本節的其餘部分不適用於您。
有兩種情況,多位元組和單位元組地區設定。首先是多位元組
Perl 可能支援的唯一多位元組 (或寬字元) 區域設定是 UTF-8。這是由於實作的困難性,事實上,現在已為世界各地發布高品質的 UTF-8 區域設定 (https://unicode.org/Public/cldr/2.0.1/ 已設定的,但來自較早版本;https://unicode.org/Public/cldr/latest/ 最新版本,但您必須自行擷取 POSIX 資訊),如果所有這些都失敗,您可以使用 Encode 模組轉譯至/從您的區域設定。因此,如果您使用其中一個區域設定,例如 Big5 或 Shift JIS,您必須執行其中一項。對於 UTF-8 區域設定,在不支援完整 UTF-8 區域設定的 Perl (v5.20 之前),它們可能運作得相當好 (視您的 C 函式庫實作而定),僅是因為它們和 Perl 都以相同方式儲存佔用多個位元組的字元。然而,大部分 C 函式庫實作(如果不是全部的話)可能無法在 LC_CTYPE
下正確處理拉丁語系 1 範圍 (128 - 255) 上半部的字元。若要查看字元是否為區域設定下的特定類型,Perl 會使用類似 isalnum()
的函式。您的 C 函式庫可能無法使用這些函式處理 UTF-8 區域設定,而只能在 Perl 未使用的較新的寬函式庫(例如 iswalnum()
)下運作。這些多位元組區域設定會像單位元組區域設定一樣處理,並會有以下所述的限制。從 Perl v5.22 開始,當 Perl 偵測到它不完全支援的多位元組區域設定時,會提出警告訊息。
對於單位元組區域設定,Perl 通常採取的策略是對可以放入單一位元組的碼點使用區域設定規則,而對無法放入的碼點使用 Unicode 規則 (雖然這並未統一套用,請參閱本節最後的附註)。這可防止在非 UTF-8 區域設定中出現許多問題。假設區域設定是 ISO8859-7,希臘語。那裡的 0xD7 字元是大寫的 Chi。但在 ISO8859-1 區域設定,拉丁語系 1,它是一個乘號。POSIX 正規表示式字元類別 [[:alpha:]]
會神奇地比對希臘語區域設定中的 0xD7,但不會比對拉丁語系區域設定中的 0xD7。
然而,在某些地方會發生中斷。某些 Perl 建構僅適用於 Unicode,例如 \p{Alpha}
。它們假設 0xD7 始終具有其 Unicode 含義(或在 EBCDIC 平台上的等效含義)。由於 Latin1 是 Unicode 的子集,且 0xD7 是 Latin1 和 Unicode 中的乘號,因此 \p{Alpha}
絕不會與它匹配,無論區域設定為何。\N{...}
也會發生類似的問題。因此,在 v5.20 之前,在純粹的 use locale
下使用 \p{}
或 \N{}
是個壞主意,除非您可以保證區域設定將為 ISO8859-1。請改用 POSIX 字元類別。
此方法的另一個問題是,跨越單位元組/多位元組邊界的運算未定義明確,因此不允許。(此邊界位於 255/256 的碼點之間。)例如,將 LATIN CAPITAL LETTER Y WITH DIAERESIS(U+0178)轉換為小寫應傳回 LATIN SMALL LETTER Y WITH DIAERESIS(U+00FF)。但在希臘區域設定中,例如,0xFF 沒有字元,而 Perl 無法得知 0xFF 處的字元實際上應該代表什麼。因此它不允許此運算。在此模式中,U+0178 的小寫字母就是它本身。
如果您在非 ISO8859-1、非 UTF-8 區域設定中啟用標準檔案控制代碼、預設 open()
層和 @ARGV
的自動 UTF-8 化(透過使用 -C 命令列開關或 PERL_UNICODE
環境變數;請參閱 perlrun),也會產生相同的問題。資料會以 UTF-8 讀取,這通常表示 Unicode 解釋,但區域設定的存在會導致它們改為以該區域設定進行解釋。例如,Unicode 輸入中的 0xD7 碼點應表示乘號,但 Perl 在希臘區域設定下不會以這種方式解釋它。只要您確定所有區域設定都將始終且僅為 ISO8859-1,或者如果您沒有有缺陷的 C 函式庫,則 UTF-8 區域設定就不是問題。
另一個問題是,此方法可能導致兩個碼點表示同一個字元。因此,在希臘區域設定中,U+03A7 和 U+00D7 都是 GREEK CAPITAL LETTER CHI。
由於所有這些問題,從 v5.22 開始,當在單位元組區域設定生效時使用多位元組(因此是 Unicode)碼點時,Perl 會發出警告。(儘管如果這樣做會不合理地降低執行速度,它不會檢查這一點。)
廠商區域設定出了名的有問題,而 Perl 很難測試其區域設定處理程式碼,因為這會與 Perl 無法控制的程式碼互動;因此,Perl 中的區域設定處理程式碼也可能會有問題。(但是,Unicode 提供的區域設定應該會更好,並且有一個回饋機制來修正任何問題。請參閱 "自由提供的區域設定定義"。)
如果您有 Perl v5.16,如果您對區域設定實用程式使用 :not_characters
參數,則上述問題將消失(非字元部分的廠商錯誤除外)。如果您沒有 v5.16,而且您確實有可用的區域設定,只要您記住前面提到的陷阱,使用它們可能會對某些特定目的有所幫助。例如,如果區域設定的排序規則有效,則在區域設定下執行速度會比在 Unicode::Collate 下執行速度更快;而且您可以存取諸如當地貨幣符號以及星期幾和月份的名稱等內容。(但要強調一點,在 v5.16 中,您可以透過使用實用程式的 :not_characters
形式來存取這些內容,而不會有區域設定的缺點。)
注意:對於可以放入一個位元組的碼點,使用地區規則的政策,而對於不能放入一個位元組的碼點,使用 Unicode 規則,這項政策並未統一套用。在 v5.12 之前,這項政策有點隨機;在 v5.12 中,它相當一致地套用於正規表示式比對,但方括號字元類別除外;在 v5.14 中,它擴充套用到所有正規表示式比對;在 v5.16 中,它擴充套用到大小寫轉換作業,例如 \L
和 uc()
。對於校對,在所有版本中,都會呼叫系統的 strxfrm()
函式,而它執行的動作就是您所得到的結果。從 v5.26 開始,修正了 perl 使用此函式的各種錯誤。
NUL
字元的字串校對NUL
字元會與最低的校對控制字元相同,或在不太可能的情況下,地區設定中完全沒有控制字元,則會與 "\001"
相同。在字串不包含此非 NUL
控制字元的情況下,結果會正確,而且在許多地區設定中,這個控制字元(無論是什麼)都很少會遇到。但是,在某些情況下,NUL
應該在這個控制字元之前進行校對,但卻沒有。如果兩個字串進行校對後完全相同,則包含 NUL
的字串會先進行校對。在 5.26 之前,有更多錯誤。
從中呼叫的 XS 程式碼或 C 語言函式庫,如果使用系統 setlocale(3)
函式(Windows 除外),在未變更的情況下,可能無法在多執行緒應用程式中執行。請參閱 perlxs 中的「地區設定感知 XS 程式碼」。
依賴地區設定的 XS 模組可能是在假設永遠不會在多執行緒環境中呼叫的情況下編寫的,因此使用其他非地區設定建構,而這些建構並非多執行緒安全的。請參閱 perlxs 中的「執行緒感知系統介面」。
POSIX 沒有定義取得目前每個執行緒地區設定名稱的方法。有些系統,例如 Darwin 和 FreeBSD,會實作一個函式 querylocale(3) 來執行此動作。在沒有此函式的非 Windows 系統上,例如 Linux,還有一些額外的注意事項
嵌入式 perl 需要在全域地區設定生效時啟動。請參閱 perlembed 中的「在 POSIX 地區設定中使用嵌入式 Perl」。
對於 perl 而言,了解平台上所有可能的區域類別變得更加重要,即使您的程式中明顯沒有使用這些類別。Perl 知道所有 Linux 的類別。如果您的平台有其他類別,您可以在 https://github.com/Perl/perl5/issues 提交問題,以將其納入下一個版本。在此同時,可以編輯 Perl 原始碼來教導它有關類別的知識,然後重新編譯。搜尋原始碼中例如 LC_PAPER
的執行個體,並將其用作範本來新增遺漏的類別。
雖然困難,但仍有可能使用它不識別為語法合法,但實際上在該系統上合法的區域設定來呼叫 POSIX::setlocale
。這只會發生在嵌入式 Perl 中,或如果你自己手動製作區域設定名稱。
在某些系統中,作業系統的區域設定支援已損壞,無法由 Perl 修復或使用。當 use locale
生效時,此類缺陷可能會導致神秘的暫停和/或 Perl 核心傾印。遇到此類系統時,請詳細報告到 <https://github.com/Perl/perl5/issues>,並聯繫你的供應商:作業系統中可能存在這些問題的錯誤修復。有時,此類錯誤修復稱為作業系統升級。如果你有 Perl 的原始碼,請在錯誤報告中包含 "測試損壞的區域設定" 中上面描述的測試的輸出。
I18N::Langinfo、perluniintro、perlunicode、open、POSIX 中的 "localeconv"、POSIX 中的 "setlocale"、POSIX 中的 "strcoll"、POSIX 中的 "strftime"、POSIX 中的 "strtod"、POSIX 中的 "strxfrm"。
當 Perl 嵌入在 C 程式中時,請參閱 perlembed 中的 "使用嵌入式 Perl 與 POSIX 區域設定",以了解特別考量事項。
Dominic Dunlop 在 perl5-porters 的協助下,大幅修改了 Jarkko Hietaniemi 的原始 perli18n.pod。Tom Christiansen 對散文進行了一些修改,現在由 Perl 5 移植者維護。