哪些组件使用区域设置变量?

我已经读过每个进程都有一组与之关联的语言环境变量。 例如,这些是与我的系统上的bash进程关联的语言环境变量:

 $ locale LANG="en_GB.UTF-8" LC_COLLATE="en_GB.UTF-8" LC_CTYPE="en_GB.UTF-8" LC_MESSAGES="en_GB.UTF-8" LC_MONETARY="en_GB.UTF-8" LC_NUMERIC="en_GB.UTF-8" LC_TIME="en_GB.UTF-8" LC_ALL= 

我想知道谁实际使用这些语言环境变量。

C标准函数(例如: fwrite() )和Linux系统调用是否使用它们? 某些C标准函数或某些Linux系统调用的行为是否因某些语言环境变量的值而异?

或者只是某些程序可以使用这些区域设置变量? 例如,我可以编写一个程序,根据LANG语言环境变量的值,以不同的语言向用户显示消息。

默认情况下,C的标准库函数使用“C”语言环境 。 您可以将其切换到用户区域设置以启用特定于语言环境的:

  • 字符处理
  • 整理
  • 日期/时间格式
  • 数字编辑
  • 货币格式
  • 消息

POSIX setlocale文档包含受其影响的区域设置相关函数的不完整列表:

catopen,exec,fprintf,fscanf,isalnum,isalpha,isblank,iscntrl,isdigit,isgraph,islower,isprint,ispunct,isspace,isupper,iswalnum,iswalpha,iswblank,iswcntrl,iswctype,iswdigit,iswgraph,iswlower,iswprint,iswpunct, iswspace,iswupper,iswxdigit,isxdigit,localeconv,mblen,mbstowcs,mbtowc,newlocale,nl_langinfo,perror,psiginfo,setlocale,strcoll,strerror,strfmon,strftime,strsignal,strtod,strxfrm,tolower,toupper,towlower,towupper,uselocale, wcscoll,wcstod,wcstombs,wcsxfrm,wctomb

例如:

 printf("%'d\n", 1000000000); printf("Setting LC_ALL to %s\n", getenv("LANG")); setlocale(LC_ALL, ""); // Set user-preferred locale. printf("%'d\n", 1000000000); 

输出:

 1000000000 Setting LC_ALL to en_US.UTF-8 1,000,000,000 

许多程序设置了语言环境,并至少将其用于国际化。 一些具体的例子:

 LANG="en_GB.UTF-8" 

这是您没有专门设置为其他类别的任何类别的区域设置。 它允许系统以向后兼容的方式添加新的语言环境变量。

 LC_COLLATE="en_GB.UTF-8" 

这将选择在字符串上使用哪种语言的排序顺序。 例如, Ch被认为是西class牙语的一封信,并且会在Cz之后出现。 使用它的一个C库函数是strcoll() ,以及包含ls (按名称排序文件)和sort POSIX命令。

 LC_CTYPE="en_GB.UTF-8" 

这决定了当前的字符编码。 在C11中,您可以设置它,然后使用宽字符输入和输出,例如wprintf() 。 该库将透明地转换宽字符和外部世界使用的字符集。 这仍然不适用于Windows,除非你做一些额外的魔术,但在其他地方,UTF-8已经成为标准。 越来越多的程序,如clang(从版本7开始),不再支持UTF-8以外的任何程序。

 LC_MESSAGES="en_GB.UTF-8" 

这确定了您在本地消息中看到的语言和字符集。在Unix / Linux上的C中,这些通常由gettext库从.po文件加载。

 LC_MONETARY="en_GB.UTF-8" 

这会影响strfmon()格式化货币数量的方式。

 LC_NUMERIC="en_GB.UTF-8" 

这决定了不是金额的数字的格式 。

 LC_TIME="en_GB.UTF-8" 

这会影响时间格式。 尝试在shell中查看LC_TIME=fr_FR.UTF-8 date 。 (或者使用locale -a | grep UTF来选择一些适合异国情调的语言环境。)同时也是一个很好的测试,你的时区和ntpd是否正常工作。

 LC_ALL= 

使用LANG代替此。 它一次设置每个语言环境类别,但它会覆盖所有其他语言环境变量中的值。 它的存在是为了向后兼容。

例如,我在我的Linux LANG=en_US.utf8上使用LANG=en_US.utf8 ,但我覆盖LC_TIME=en_GB.utf8以获得24小时英语时间。 如果设置了LC_ALL则无法执行此操作。

LANG还允许您的默认值转换为系统支持的任何其他语言环境信息,例如LC_ADDRESSLC_IDENTIFICATIONLC_RESPONSELC_MEASUREMENTLC_TELEPHONE

我已经读过每个进程都有一组与之关联的语言环境变量。

这不是真的,或者至少它是高度过度简化的。

许多标准库函数(和非标准库函数)基于一组区域设置配置修改其行为,这些配置在标准库实现中的某个隐藏全局对象中维护。 (在某些库实现中,使用线程局部静态变量,每个线程而不是全局维护语言环境配置。)这似乎与进程相关联,因为通常每个进程都有一个标准库运行时的实例,但重要的是要了解 – 尽管有外观 – 语言环境支持是库的一部分,而不是操作系统内核。 (当然,任何标准中没有任何内容定义内核的边界,甚至内核可能是什么。您可以运行程序“裸机”或者您可能有一个操作系统认为在系统调用中实现标准库很有用我在这里谈论常见案例。)

基本语言环境配置由第7.11节(C11标准)中的C标准定义,该标准定义了两个接口:

  • setlocale ,修改库的语言环境配置,和

  • localeconv ,它查询部分语言环境配置,允许用户代码符合语言环境的数字格式约定(包括货币格式)。

区域设置配置分为多个或多或少的独立组件,称为“类别”。 (C ++标准库称这些“facets”,这也是一个常用词。)C标准定义了五个类别,Posix定义了一个类别,但类别是开放式的; 单个标准库实现可以自由添加其他类别。 例如,大多数Linux系统上使用的Gnu标准C库目前总共有12个类别。 (有关当前列表,请参阅系统上的man 7 locale 。)

标准类别是:

  • LC_CTYPE :字符分类和大小写转换。
  • LC_COLLATE :整理顺序。
  • LC_MONETARY :货币格式。
  • LC_NUMERIC :数字,非货币格式。
  • LC_TIME :日期和时间格式。

和Posix扩展名是:

  • LC_MESSAGES :信息和诊断消息以及交互式响应的格式。

除了localeconv (仅提供对LC_NUMERICLC_MONETARY类别的特定配置的访问)之外,无法查询任何特定配置。

此外,没有标准的方法来设置单个配置。 您所能做的就是使用setlocale来配置整个类别,使用依赖于库和非标准化的区域设置名称(这只是一个字符串)。 更确切地说,两个区域设置名称是标准化的:

  • C标准定义了语言环境名称C

  • Posix定义了语言环境名称POSIX 。 但是,Posix指定相应的语言环境应与名为C的语言环境相同。

语言环境命名的详细信息是(或应该)在您正在使用的环境的locale文档中详细说明,但通常,语言环境感知程序永远不会使用除标准名称之外的字符串常量调用setlocale ,或者为空串。 (我会在一分钟内完成。)

setlocale接口允许程序设置单个区域设置类别,或将所有区域设置类别设置为相同的区域设置名称。 它还返回一个字符串,该字符串可用于返回先前配置的区域设置类别(或完整配置)。

上面类别列表中显示的类别名称是定义的宏。 另一个宏LC_ALL也由该头文件定义: LC_ALL 。 其中一个宏必须用作setlocale的第一个参数。

C和Posix标准都要求程序启动时的初始语言环境设置是C语言环境。 C语言环境的许多方面都是标准化的(并且Posix语言环境的某些方面是标准化的)。 例如,这种标准化允许程序员预测数字转换的工作方式。

但通常情况下,程序员希望使用该用户自己的语言环境首选项与程序的用户进行交互。 显然不希望每个程序都有自己的特殊机制来确定用户的语言环境首选项是什么,因此标准库提供了一种机制,用于将语言环境(或单个语言环境类别)设置为默认语言环境配置的任何内容:调用setlocale ,其中空字符串( "" )作为区域设置名称。 C标准没有规定配置此信息的任何特定机制; 它只是假设一个存在。

(旁注:使用空字符串作为语言环境名称调用setlocale与使用NULL作为语言环境名称调用setlocale告诉setlocale不更改任何语言环境设置,但它仍将返回与当前语言环境关联的字符串。避免使用getlocale接口。)

Posix确实指定了一种配置用户首选项的机制,并且还坚持(大多数)标准化命令行实用程序在默认语言环境中运行。 该机制使用其名称对应于setlocale类别宏的环境变量。

在Posix实现上,当程序调用setlocale(LC_X, ""); 图书馆将继续检查当前的环境:

  1. 首先,它查找环境变量LC_ALL 。 如果已定义并且具有非空值,则它用于定义区域设置。

  2. 否则,如果setlocale的第一个参数不是LC_ALL它将查找其名称与该参数相同的环境变量。 如果已定义并且具有非空值,则它用于定义区域设置。

  3. 否则,如果定义了环境变量LANG并且具有非空值,则使用它(以某种实现依赖方式)来构造区域设置名称。 ( LANG应该表示用户的语言,这是他们的语言环境偏好的重要部分。)

  4. 最后,使用了一些系统范围的默认值。

环境变量通常由系统配置文件的login程序(或等效的GUI)初始化。 (精确的机制因分布而异,通常很难找到文档。)

如上所述,Posix几乎需要所有标准shell实用程序来执行相当于setlocale(LC_ALL, ""); 以便在用户配置的区域设置中操作。 每个实用程序的联机帮助页(或其他文档)应指定它是否执行此操作,但除非有相反的信息,否则可以合理地假设它。

此外,许多(但不是全部)标准库字符串函数都是区域设置感知的。 肯定不是语言环境感知的库接口包括isdigitisxdigit ,它们始终基于C语言环境进行响应, strcmp使用char值(解释为unsigned int )来比较字符串,方法与memcmp相同。确定整理顺序。 ( strcoll是区域设置感知的,如果你想根据LC_COLLATE进行比较。)并且用于宽字节和多字节字符的字符编码由LC_CTYPE类别控制(以某种未指定的方式)。