内容

名称

perllocale - Perl 区域设置处理(国际化和本地化)

说明

起初有 ASCII,“美国信息交换标准代码”,它非常适合使用英语字母和以美元计价的货币的美国人。但即使对于其他英语使用者来说,它也不太适用,他们可能使用不同的货币,例如英镑(因为该货币的符号不在 ASCII 中);并且它对于世界上其他数千种语言来说是完全不够的。

为了解决这些缺陷,发明了区域设置的概念(正式的 ISO C、XPG4、POSIX 1.c “区域设置系统”)。并且已经编写并正在编写使用区域设置机制的应用程序。使此类应用程序考虑其用户在这些方面的偏好的过程称为国际化(通常缩写为i18n);告诉此类应用程序有关特定偏好集的信息称为本地化l10n)。

Perl 已得到扩展以支持区域设置系统中可用的某些类型的区域设置。这由每个应用程序使用一个编译指示、一个函数调用和几个环境变量来控制。

Perl 支持单字节区域设置,它们是 ASCII 的超集,例如 ISO 8859 区域设置,以及一个多字节类型区域设置,即 UTF-8 区域设置,在下一段中进行了描述。Perl 不支持任何其他多字节区域设置,例如东亚语言的区域设置。

不幸的是,区域设置的设计(以及通常的实现)存在很多缺陷。Unicode 的发明(请参阅 perlunitut 以了解有关它的介绍)部分是为了解决这些设计缺陷,如今,有一系列基于 Unicode 的“UTF-8 区域设置”。这些区域设置的字符集是 Unicode,编码为 UTF-8。从 v5.20 开始,Perl 完全支持 UTF-8 区域设置,但排序和字符串比较(如 ltge)除外。从 v5.26 开始,Perl 也可以合理地处理这些问题,具体取决于平台的实现。但是,对于早期版本或为了更好地控制,请使用 Unicode::Collate。实际上有两种略有不同的 UTF-8 区域设置类型:一种用于突厥语,另一种用于其他所有语言。

从 Perl v5.30 开始,Perl 通过其行为检测突厥语区域设置,并无缝地处理这两种类型;以前仅支持非突厥语区域设置。如果您的系统具有 tr_TR.UTF-8 区域设置并且它不像突厥语区域设置那样,则 Perl 会将其视为非突厥语区域设置,而忽略区域设置的名称。

Perl 继续支持旧的非 UTF-8 区域设置。目前对于 EBCDIC 平台没有 UTF-8 区域设置。

(Unicode 还在创建“通用区域设置数据存储库”CLDRhttp://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:错误和其他消息

Perl 本身仅通过 $!$^E 访问操作系统错误消息时使用此类别。

类别 LC_COLLATE:排序

这表示字母的排序顺序以进行比较和排序。例如,在拉丁字母表中,“b” 通常跟在“a”之后。

类别 LC_CTYPE:字符类型

例如,这表示字符是否是大写字母。

其他类别

某些平台有其他类别,用于处理诸如测量单位和纸张大小之类的事物。Perl 不会直接使用这些类别,但 Perl 交互的外部操作可能会使用这些类别。请参见下文的“不在“use locale”的范围内”。

"区域设置类别" 中,将详细介绍 Perl 使用的类别。

这些类别共同为自定义单个程序以在许多不同位置运行提供了很大帮助。但仍存在不足,因此请继续阅读。

准备使用区域设置

Perl 本身(POSIX 模块除外)不会使用区域设置,除非特别要求(但再次注意,Perl 可能会与使用区域设置的代码交互)。即使有这样的请求,也必须满足以下所有条件才能正常工作

如果您希望 Perl 应用程序根据特定区域设置处理和显示您的数据,则应用程序代码应在适当的位置包含 use locale pragma(请参阅 "The "use locale" pragma"),并且至少满足以下条件之一

  1. 由您或设置您的系统帐户的人在应用程序启动时正确设置确定区域设置的环境变量(请参阅 "ENVIRONMENT";或

  2. 应用程序必须使用 "The setlocale function" 中描述的方法设置其自己的区域设置

使用区域设置

"use locale" pragma

从 Perl 5.28 开始,可以在具有线程安全区域设置功能的系统上的多线程应用程序中使用此编译指示。有些注意事项,请参见下面的"多线程"。在没有此功能的系统上,或在较早的 Perl 中,请勿在有多个线程处于活动状态的脚本中使用此编译指示。在这种情况下,区域设置不属于单个线程。另一个线程可能随时更改区域设置,这至少会导致给定线程在它不期望所在的区域设置中运行。在某些平台上,还可能发生段错误。区域设置更改不必是显式的;某些操作会导致 perl 本身更改区域设置。只需执行"use locale",您就容易受到攻击。

默认情况下,Perl 本身(POSIX模块之外)会忽略当前区域设置。use locale编译指示告诉 Perl 对某些操作使用当前区域设置。从 v5.16 开始,此编译指示有一些可选参数,如下所述,这些参数限制了受其影响的操作。

当前区域设置在执行时由下面描述的setlocale()设置。如果在程序执行过程中尚未调用该函数,则当前区域设置是由程序开始时生效的"ENVIRONMENT"确定的。如果没有有效的环境,则当前区域设置是系统默认设置的任何内容。在 POSIX 系统上,它可能是(但不一定)"C" 区域设置。在 Windows 上,默认设置通过计算机的控制面板->区域和语言选项(或其当前等效项)设置。

受区域设置影响的操作是

不在"use locale"范围内

如下所示,只有某些操作(全部源自 Perl 之外)应受到影响

  • 如果这些操作区分区域设置,则在使用system()qx//等操作时,将使用当前区域设置。

  • Perl 还通过 POSIX 模块访问各种 C 库函数。其中一些函数始终受当前区域设置影响。例如,POSIX::strftime() 使用 LC_TIMEPOSIX::strtod() 使用 LC_NUMERICPOSIX::strcoll()POSIX::strxfrm() 使用 LC_COLLATE。所有此类函数都将根据当前底层区域设置执行,即使该区域设置未公开给 Perl 空间。

    这也适用于 I18N::Langinfo

  • LC_NUMERIC 之外的所有类别的 XS 模块都获取底层区域设置,因此他们调用的任何 C 库函数都将使用该底层区域设置。有关更多讨论,请参阅 perlxs 中的“CAVEATS”

请注意,所有 C 程序(包括用 C 编写的 perl 解释器)始终都有一个底层区域设置。除非通过调用 setlocale() 更改,否则该区域设置是“C”区域设置。当 Perl 启动时,它将底层区域设置更改为 "ENVIRONMENT" 指示的区域设置。在使用 POSIX 模块或编写 XS 代码时,务必记住底层区域设置可能是除“C”之外的其他内容,即使程序没有明确更改它。

use locale 的持续效果

use locale 范围内设置的某些 Perl 操作即使在范围外也会保留该效果。其中包括

  • write() 的输出格式由较早的格式声明确定(perlfunc 中的“format”),因此输出是否受区域设置影响取决于 format() 是否在 use locale 的范围内,而不是 write() 是否在范围内。

  • 可以使用 qr// 编译正则表达式模式,实际匹配延迟到以后。同样,是否在 use locale 的范围内完成编译决定了匹配行为,而不是是否在这样的范围内完成匹配。

"use locale";
  • 所有上述操作

  • 格式声明perlfunc 中的“format”),因此任何后续的 write() 都使用 LC_NUMERIC

  • 字符串化和输出使用 LC_NUMERIC。其中包括 print()printf()say()sprintf() 的结果。

  • 比较运算符ltlecmpgegt)使用 LC_COLLATE。如果在没有显式比较函数的情况下使用 sort(),它也会受到影响,因为它默认使用 cmp

    注意:eqne 不受区域设置影响:它们始终对标量操作数执行逐字符比较。此外,如果 cmp 发现其操作数根据当前区域设置指定的排序顺序相等,它将继续执行逐字符比较,并且仅当操作数逐字符完全相同时才返回 0(相等)。如果您真的想知道两个字符串(eqcmp 可能认为不同)在区域设置的排序中是否相等,请参阅 "类别 LC_COLLATE:排序" 中的讨论。

  • 正则表达式和大小写转换函数uc()lc()ucfirst()lcfirst())使用 LC_CTYPE

  • 变量 $!(及其同义词 $ERRNO$OS_ERROR $^E>(及其同义词 $EXTENDED_OS_ERROR)用作字符串时使用 LC_MESSAGES

使用 no locale pragma 或在到达包含 use locale 的块的末尾时恢复默认行为。请注意,use locale 调用可以嵌套,并且内部范围内的内容将在内部范围的末尾恢复到外部范围的规则。

使用区域设置信息的任何操作的字符串结果都是受污染的(如果您的 perl 支持污染检查),因为区域设置可能是不可信的。请参阅 "SECURITY"

从 Perl v5.16 开始,以非常有限的方式,在 v5.22 中更普遍,您可以通过向 pragma 添加参数来限制此特定 pragma 实例启用的类别或类别。例如,

use locale qw(:ctype :numeric);

在其范围内启用区域设置感知,仅限于受 LC_CTYPELC_NUMERIC 影响的那些操作(如上所列)。

可能的类别有::collate:ctype:messages:monetary:numeric:time 和伪类别 :characters(如下所述)。

因此,您可以说

use locale ':messages';

并且只有 $!$^E 会具有区域设置感知。其他所有内容不受影响。

由于 Perl 目前不会对 LC_MONETARY 类别做任何处理,因此指定 :monetary 实际上什么都不做。某些系统有其他类别,例如 LC_PAPER,但 Perl 也不会对它们做任何处理,并且无法在该 pragma 的参数中指定它们。

您还可以轻松地说使用所有类别,但一个类别,例如,

use locale ':!ctype';
use locale ':not_ctype';

这两个都意味着启用所有类别的区域设置感知,但 LC_CTYPE 除外。如果 use locale 采用否定形式,则只能在其中指定一个类别参数。

在 v5.22 之前,只有一种带有参数的 pragma 形式可用

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_CTYPELC_COLLATE类别。相反,它将使用本机字符集(由 Unicode 扩展)。使用此参数时,您负责将外部字符集转换为本机/Unicode 字符集(如果它是越来越流行的 UTF-8 区域设置之一,它已经是这样了)。有方便的方法可以做到这一点,如"Unicode 和 UTF-8"中所述。

setlocale 函数

警告!在 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 以使用旧的非线程安全实现,方法是向 Configure 传递参数 -Accflags='-DNO_THREAD_SAFE_LOCALE'。除了 Windows 外,这将在某些情况下继续使用 POSIX 2008 的某些函数。如果这些函数存在缺陷,您可以向 Configure 传递以下内容,也可以另外传递:-Accflags='-DNO_POSIX_2008_LOCALE'。这还将阻止代码使用线程安全区域设置。在关闭线程安全操作的系统上,${^SAFE_LOCALES} 将为 0。

通常在非线程化构建中,使用的是传统的 setlocale(),而不是线程安全区域设置函数。您可以在具有这些函数的系统上强制使用这些函数,方法是向 Configure 添加 -Accflags='-DUSE_THREAD_SAFE_LOCALE'

初始程序使用从环境中指定的区域设置启动,如当前在 "ENVIRONMENT" 中所述。所有新创建的线程都以将 LC_ALL 设置为 "C" 开始。每个线程都可以随时使用 POSIX::setlocale() 来查询或切换其区域设置,而不会影响任何其他线程。所有与区域设置相关的操作都会自动使用其线程的区域设置。

对于完全用 Perl 编写的任何应用程序,这都应该是完全透明的(减去 "Multi-threaded" 部分中给出的少数罕见警告)。有关 XS 模块编写者的信息,请参阅 "perlxs 中的区域设置感知 XS 代码"

查找区域设置

对于系统中可用的区域设置,还可以查阅 setlocale(3),以查看它是否会生成可用区域设置的列表(搜索 SEE ALSO 部分)。如果失败,请尝试以下命令行

        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() 的调用接口已经标准化,但区域设置的名称和配置所在的目录尚未标准化。名称的基本形式是 language_territory.codeset,但 language 之后的某些部分并不总是存在的。languagecountry 通常来自标准 ISO 3166ISO 639,它们分别是国家和世界语言的两位缩写。codeset 部分通常会提及一些 ISO 8859 字符集,即拉丁字符集。例如,ISO 8859-1 是所谓的“西欧字符集”,可用于充分编码大多数西欧语言。同样,即使是该标准的名称,也有多种写法。令人遗憾的是。

值得特别注意两个特殊区域设置:“C”和“POSIX”。目前,它们实际上是相同的区域设置:主要区别在于前者由 C 标准定义,后者由 POSIX 标准定义。它们定义了默认区域设置,在环境中没有区域设置信息时,每个程序都将在此区域设置中启动。(如果您愿意,可以将其称为默认默认区域设置。)其语言为(美国)英语,其字符编码集为 ASCII 或(极少见)其超集(例如“DEC 多国字符集 (DEC-MCS)”。警告。某些供应商提供的 C 区域设置可能与 C 标准要求的不完全匹配。因此,请注意。

注意:并非所有系统都具有“POSIX”区域设置(并非所有系统都符合 POSIX),因此,当您需要明确指定此默认区域设置时,请使用“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").

这意味着您的区域设置将 LC_ALL 设置为“En_US”,并且 LANG 存在但没有值。Perl 尝试相信您,但无法相信。相反,Perl 放弃并退回到“C”区域设置,即无论如何都应该工作的默认区域设置。(在 Windows 上,它首先尝试退回到系统默认区域设置。)这通常意味着您的区域设置错误,它们提到了您的系统从未听说过的区域设置,或者您系统中的区域设置安装有问题(例如,某些系统文件已损坏或丢失)。对于这些问题,有一些快速且临时的修复方法,以及一些更彻底且持久的修复方法。

测试损坏的区域设置

如果您从源代码构建 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_BADLANG 设置为“0”或“”来让 Perl 对区域设置问题保持沉默。此方法实际上只是将问题扫到地毯下:即使 Perl 发现某些内容出现问题,您也会告诉 Perl 保持沉默。如果稍后某些依赖于区域设置的内容出现异常,请不要感到惊讶。

可以通过将环境变量 LC_ALL 设置为“C”在“C”区域设置下运行 Perl。此方法可能比 PERL_BADLANG 方法更文明一些,但设置 LC_ALL(或其他区域设置变量)也可能会影响其他程序,而不仅仅是 Perl。特别是,从 Perl 中运行的外部程序将看到这些更改。如果您将新设置设为永久(请继续阅读),您运行的所有程序都将看到这些更改。有关相关环境变量的完整列表,请参见"ENVIRONMENT",有关它们在 Perl 中的效果,请参见"USING LOCALES"。其他程序中的效果很容易推断出来。例如,变量 LC_COLLATE 可能会影响您的sort程序(或您的系统中按字母顺序排列“记录”的任何程序)。

您可以暂时测试更改这些变量,如果新设置似乎有帮助,请将这些设置放入您的 shell 启动文件中。查阅您的本地文档以了解确切的详细信息。对于类似 Bourne 的 shell(shkshbashzsh

LC_ALL=en_US.ISO8859-1
export LC_ALL

这假设我们使用上面讨论的命令看到了区域设置“en_US.ISO8859-1”。我们决定尝试使用它来代替上述错误的区域设置“En_US”——以及在 Cshish shell(cshtcsh)中

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”之类的东西,但那不是一回事。在这种情况下,请尝试在您可以列出且在某种程度上与您尝试的内容匹配的区域设置下运行。匹配区域设置名称的规则有点模糊,因为在这个领域标准化很弱。请再次参阅"查找区域设置"了解一般规则。

修复系统区域设置配置

联系系统管理员(最好是您自己的),报告您收到的确切错误消息,并要求他们阅读您现在正在阅读的相同文档。他们应该能够检查系统区域设置配置是否有问题。"查找区域设置" 部分不幸的是对确切命令和位置有点含糊,因为这些东西没有那么标准化。

localeconv 函数

POSIX::localeconv() 函数允许您获取当前底层 LC_NUMERICLC_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_pointthousands_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

查询与区域设置相关信息的另一个接口是 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

区域设置类别

以下小节描述了基本的区域设置类别。除此之外,一些组合类别允许一次操作多个基本类别。请参阅 "ENVIRONMENT" 以了解有关它们的讨论。

类别 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 将为真。

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 魔术(请参阅 perlguts 中的“魔术变量”)在比较中首次需要转换后的字符串版本时创建该版本,然后在需要时保留此版本。使用 cmp 以简单的方式重写的示例运行速度几乎一样快。它还可以处理字符串中嵌入的空字符;如果你直接调用 strxfrm(),它会将找到的第一个空字符视为终止符。不要指望它产生的转换后的字符串可以在系统之间移植——甚至不能从操作系统的某个修订版移植到下一个修订版。简而言之,不要直接调用 strxfrm():让 Perl 为你执行此操作。

注意:某些示例中未显示 use locale,因为它不需要:strcoll()strxfrm() 是 POSIX 函数,它们使用始终遵守当前 LC_COLLATE 区域设置的标准系统提供的 libc 函数。

类别 LC_CTYPE:字符类型

在包含 LC_CTYPEuse 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 区域设置生效(污损除外;请参见 "SECURITY")。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 and UTF-8")。

请注意,当前区域设置不受很多因素影响。任何文字字符都是给定平台的本机字符。因此,'A' 表示 ASCII 平台上的代码点 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 正则表达式修饰符一起使用。参见 "SECURITY"

类别 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::LanginfoRADIXCHAR

类别 LC_MONETARY:货币金额格式化

C 标准定义了 LC_MONETARY 类别,但没有受其内容影响的函数。(有标准委员会经验的人会认识到,工作组决定回避这个问题。)因此,Perl 基本上不注意它。如果您真的想使用 LC_MONETARY,您可以查询其内容——参见 "localeconv 函数"——并在应用程序自己的货币金额格式化中使用它返回的信息。但是,您可能会发现,尽管信息内容丰富且复杂,但仍然不能完全满足您的要求:货币格式化是一个难以破解的难题。

另请参见 I18N::LanginfoCRNCYSTR

类别 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 localestrftime() 是一个 POSIX 函数,它使用始终遵循当前 LC_TIME 区域设置的标准系统提供的 libc 函数。

另请参见 I18N::LanginfoABDAY_1..ABDAY_7DAY_1..DAY_7ABMON_1..ABMON_12ABMON_1..ABMON_12

其他类别

Perl 自身当前不使用剩余的区域设置类别。但再次注意,Perl 交互的对象可能会使用它们,包括标准 Perl 发行版之外的扩展,以及操作系统及其实用程序。特别注意,$! 的字符串值和外部实用程序给出的错误消息可能会被 LC_MESSAGES 更改。如果您想要可移植的错误代码,请使用 %!。请参见 Errno

安全性

尽管可以在 perlsec 中找到 Perl 安全问题的讨论,但如果它没有引起您对与区域设置相关的安全问题的注意,那么对 Perl 的区域设置处理的讨论将是不完整的。区域设置——尤其是在允许非特权用户构建自己的区域设置的系统上——是不可信的。恶意(或只是已损坏)区域设置可能会使区域设置感知应用程序给出意外的结果。以下是一些可能性

此类危险并非仅限于区域设置系统:应用程序环境中任何可能被恶意修改的方面都存在类似的挑战。同样,它们也不仅限于 Perl:任何允许编写考虑其环境的程序的编程语言都会使您面临这些问题。

Perl 无法保护您免受示例中显示的所有可能性——没有替代您自己的警惕——但是,当 use locale 生效时,Perl 使用污染机制(请参阅 perlsec)来标记成为区域设置相关且因此可能不可信的字符串结果。

请注意,可以在不启用污染支持的情况下编译 Perl,在这种情况下,所有污染功能都会静默执行。

以下是可能受区域设置影响的操作符和函数的污染行为摘要

三个示例说明了与区域设置相关的污染。第一个程序忽略了其区域设置,它不会运行:当启用污染检查时,直接从命令行获取的值可能无法用于命名输出文件。

#/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";

此第三个程序无法运行,因为 $& 被污染:它是当 use locale 生效时涉及 \w 的匹配结果。

环境

PERL_SKIP_LOCALE_INIT

此环境变量在 Perl v5.20 中开始可用,如果设置(为任何值),则告诉 Perl 不要使用其他环境变量进行初始化。相反,Perl 使用当前的区域设置。这在嵌入式环境中特别有用,请参阅 "在 perlembed 中使用带有 POSIX 区域设置的嵌入式 Perl"

PERL_BADLANG

一个字符串,可以禁止 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_ALLLC_CTYPE 的情况下,LANG 会选择字符类型区域设置。

LC_COLLATE

在没有 LC_ALL 的情况下,LC_COLLATE 会选择排序(整理)区域设置。在没有 LC_ALLLC_COLLATE 的情况下,LANG 会选择排序区域设置。

LC_MONETARY

在没有 LC_ALL 的情况下,LC_MONETARY 会选择货币格式区域设置。在没有 LC_ALLLC_MONETARY 的情况下,LANG 会选择货币格式区域设置。

LC_NUMERIC

在没有 LC_ALL 的情况下,LC_NUMERIC 会选择数字格式区域设置。在没有 LC_ALLLC_NUMERIC 的情况下,LANG 会选择数字格式。

LC_TIME

在没有 LC_ALL 的情况下,LC_TIME 会选择日期和时间格式区域设置。在没有 LC_ALLLC_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.

备注

字符串 evalLC_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 就会使用它们。

I18N:Collate 已过时

在 5.004 之前的 Perl 版本中,可以使用 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 和 l10n

“国际化”通常缩写为 i18n,因为其第一个字母和最后一个字母之间隔着其他十八个字母。(您可以猜到为什么 internalin ... internaliti ... i18n 往往会被缩写。)同样,“本地化”通常缩写为 l10n

不完美的标准

国际化(如 C 和 POSIX 标准中定义的)可能被批评为不完整且笨拙。它们还倾向于像标准组一样将世界划分为国家,而我们都知道世界同样可以划分为银行家、骑自行车的人、游戏玩家等等。

Unicode 和 UTF-8

对 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,但在拉丁语区域设置中不匹配。

然而,在某些地方,这种方法会失效。某些 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 之间。)例如,将拉丁大写字母 Y 带变音符号 (U+0178) 转换为小写应返回拉丁小写字母 Y 带变音符号 (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 都是希腊大写字母 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 中,它扩展到诸如 \Luc() 等大小写操作。对于排序,在迄今为止的所有版本中,都会调用系统的 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 使用。当 use locale 生效时,此类缺陷可能会导致神秘的挂起和/或 Perl 核心转储。遇到此类系统时,请向 <https://github.com/Perl/perl5/issues> 详细报告,并联系您的供应商:您的操作系统中可能存在针对这些问题的错误修复。有时,此类错误修复被称为操作系统升级。如果您有 Perl 源代码,请在错误报告中包含 “测试损坏的区域设置” 中上面描述的测试输出。

另请参见

I18N::LanginfoperluniintroperlunicodeopenPOSIX 中的“localeconv”POSIX 中的“setlocale”POSIX 中的“strcoll”POSIX 中的“strftime”POSIX 中的“strtod”POSIX 中的“strxfrm”

当 Perl 嵌入在 C 程序中时,请参阅 perlembed 中的“使用嵌入式 Perl 和 POSIX 语言环境”,以了解特殊注意事项。

历史记录

Jarkko Hietaniemi 的原始 perli18n.pod 在 perl5-porters 的协助下,被 Dominic Dunlop 大量修改。Tom Christiansen 对散文进行了修改,现在由 Perl 5 维护人员维护。