“ LC_ALL = C”是做什么的?


324

在类Unix系统中的C价值是LC_ALL什么?

我知道它在所有方面都强制使用相同的语言环境,但是该怎么C做?


如果您想使用xclockwarning(Missing charsets in String to FontSet conversion)解决问题,则最好使用该方法LC_ALL=C.UTF-8来避免西里尔字母的问题。要设置此环境变量,必须在~/.bashrc文件末尾添加以下行export LC_ALL=C.UTF-8
fedotsoldier

@fedotsoldier,您可能应该提出问题并自己给出答案,我认为这与问题无关。这只是对您遇到的其他问题的解答。
jcubic

是的,您说得对,好吧
fedotsoldier

Answers:


209

它强制应用程序使用默认语言进行输出:

$ LC_ALL=es_ES man
¿Qué página de manual desea?

$ LC_ALL=C man
What manual page do you want?

并强制按字节排序:

$ LC_ALL=en_US sort <<< $'a\nb\nA\nB'
a
A
b
B

$ LC_ALL=C sort <<< $'a\nb\nA\nB'
A
B
a
b

20
+1是个很好的例子,但缺少Stephane的答案上的重要信息...
Olivier Dulac

4
默认语言是什么意思?
斯特凡Chazelas

2
是的,我知道作者可以做他喜欢做的任何事情,包括不能做在罐子上的话。事情是。在LC_ALL = C中,美国英语是唯一可以用字符集正确表示的语言,在LC_ALL = C(LC_COLLATE)中排序次序有意义的唯一语言,LC_ALL = C(LC_TIME)具有英语月份和日期名称。我从未见过LC_ALL = C以不同于LC_ALL = en LANGUAGE = en的语言返回消息的应用程序。如果不是这种情况,我是否有权报告程序错误?(此处不谈论未翻译成英文的应用程序)。
斯特凡Chazelas

2
问题是“美国英语是唯一可以用LC_ALL = C中的字符集正确表示的语言”。这通常仅在使用窄字符的C / C ++程序中才是正确的,但是即使那样也有例外(因为有几种语言仅使用ASCII中找到的字符和符号)。如果默认语言不是英语,则报告错误会使您看起来...偏执。
伊格纳西奥·巴斯克斯

3
请注意,在英语中(意思是LANG = en_US.utf8),消息可以(并且应该)使用unicode字符(例如“”)来引用字符串。而在LANG = C中,它仅包含ASCII(双引号,反引号和撇号)。
安赫尔

332

LC_ALL是覆盖所有其他本地化设置的环境变量(在某些情况下除外$LANGUAGE)。

可以使用一些环境变量来设置本地化的不同方面(例如千位分隔符或小数点字符,字符集,排序顺序,月份,日期名称,语言或应用程序消息,例如错误消息,货币符号)。

通常,您会$LANG使用一个标识您所在地区的值来设置自己的偏好(例如,fr_CH.UTF-8如果您使用UTF-8在瑞士法语中)。各个LC_xxx变量会覆盖某个方面。LC_ALL覆盖所有。该locale命令在不带参数的情况下给出当前设置的摘要。

例如,在GNU系统上,我得到:

$ locale
LANG=en_GB.UTF-8
LANGUAGE=
LC_CTYPE="en_GB.UTF-8"
LC_NUMERIC="en_GB.UTF-8"
LC_TIME="en_GB.UTF-8"
LC_COLLATE="en_GB.UTF-8"
LC_MONETARY="en_GB.UTF-8"
LC_MESSAGES="en_GB.UTF-8"
LC_PAPER="en_GB.UTF-8"
LC_NAME="en_GB.UTF-8"
LC_ADDRESS="en_GB.UTF-8"
LC_TELEPHONE="en_GB.UTF-8"
LC_MEASUREMENT="en_GB.UTF-8"
LC_IDENTIFICATION="en_GB.UTF-8"
LC_ALL=

我可以使用以下方法覆盖单个设置:

$ LC_TIME=fr_FR.UTF-8 date
jeudi 22 août 2013, 10:41:30 (UTC+0100)

要么:

$ LC_MONETARY=fr_FR.UTF-8 locale currency_symbol
€

或使用LC_ALL覆盖所有内容。

$ LC_ALL=C LANG=fr_FR.UTF-8 LC_MESSAGES=fr_FR.UTF-8 cat /
cat: /: Is a directory

在脚本中,如果要强制执行特定设置,因为您不知道用户强制执行了哪些设置(也可能是LC_ALL),则最好,最安全且通常唯一的选择是强制执行LC_ALL。

C区域是指是最简单的语言环境的特殊区域。您还可以说,其他语言环境适用于人类,而C语言环境适用于计算机。在C语言环境中,字符是单个字节,字符集是ASCII(不是必需的,但实际上将在我们大多数人都会使用的系统中使用),排序顺序基于字节值,该语言通常是美国英语(尽管对于应用程序消息(与月份或日期名称或系统库中的消息相反),它由应用程序作者自行决定),并且未定义货币符号之类的东西。

在某些系统上,POSIX语言环境有所不同,例如,未定义非ASCII字符的排序顺序。

通常,您使用LC_ALL = C运行命令,以避免用户的设置干扰您的脚本。举例来说,如果你想[a-z]匹配的26个ASCII字符从az,你必须设置LC_ALL=C

在GNU系统上,LC_ALL=CLC_ALL=POSIX(或LC_MESSAGES=C|POSIX)重写$LANGUAGE,而LC_ALL=anything-else不会。

通常需要设置LC_ALL=C以下几种情况:

  • sort -usort ... | uniq...。在C以外的许多语言环境中,在某些系统上(尤其是GNU语言),某些字符具有相同的排序顺序sort -u不会报告唯一行,但会报告具有相同排序顺序的每组行之一。因此,如果确实需要唯一的行,则需要一个字符为字节且所有字符具有不同排序顺序的C语言环境(该语言环境保证)。
  • 这同样适用于=运营商POSIX兼容的expr==符合POSIX标准的运营商awkS(mawk并且gawk没有在这方面POSIX),不检查两个字符串是否相同,但他们是否排序相同。
  • 字符范围如中grep。如果您要匹配用户语言中的字母,请使用grep '[[:alpha:]]',不要修改LC_ALL。但是,如果要匹配a-zA-ZASCII字符,则需要LC_ALL=C grep '[[:alpha:]]'LC_ALL=C grep '[a-zA-Z]'¹。[a-z]匹配之后a和之前排序的字符z(尽管使用许多API更为复杂)。在其他语言环境中,您通常不知道它们是什么。例如,某些语言环境会忽略大小写排序,因此[a-z]在某些API(例如bash模式)中,可能包含[B-Z][A-Y]。在许多UTF-8语言环境中(包括en_US.UTF-8在大多数系统上),[a-z]将包括变音符号从a到的拉丁字母,y但不包括z(因为z在它们之前排序),我无法想象这将是您想要的(为什么要包含é而不是ź?)。
  • 浮点数算法ksh93ksh93荣誉的decimal_point中设置LC_NUMERIC。如果编写包含的脚本,a=$((1.2/7))当其语言环境以逗号作为小数点分隔符的用户运行时,它将停止工作:

    $ ksh93 -c 'echo $((1.1/2))'
    0.55
    $ LANG=fr_FR.UTF-8  ksh93 -c 'echo $((1.1/2))'
    ksh93: 1.1/2: arithmetic syntax error
    

    然后,您需要类似以下内容:

    #! /bin/ksh93 -
    float input="$1" # get it as input from the user in his locale
    float output
    arith() { typeset LC_ALL=C; (($@)); }
    arith output=input/1.2 # use the dot here as it will be interpreted
                           # under LC_ALL=C
    echo "$output" # output in the user's locale
    

    附带说明:,小数点分隔符与,算术运算符冲突,这可能会引起更多混乱。

  • 当您需要字符作为字节时。如今,大多数语言环境都基于UTF-8,这意味着字符可以占用1到6个字节。使用文本实用程序处理本应为字节的数据时,您需要设置LC_ALL = C。由于解析UTF-8数据需要一定的成本,因此它也将显着提高性能。
  • 上一点的推论:处理文本时,您不知道输入的字符集是什么,但是可以假定它与ASCII兼容(因为几乎所有字符集都是如此)。例如,如果您使用的是UTF-8语言环境,grep '<.*>'而寻找包含a的行,则该对将不起作用,并且输入使用单字节8位字符集(如iso8859-15)进行编码。这是因为在iso8859-15中仅匹配字符和非ASCII字符在UTF-8中可能不会形成有效字符。另一方面,它将起作用,因为任何字节值都在语言环境中形成有效字符。<>.LC_ALL=C grep '<.*>'C
  • 任何时候处理非人为的/人为的输入数据或输出数据的时间。如果您正在与用户交谈,则可能要使用他们的约定和语言,但是,例如,如果您生成一些数字来提供其他一些期望使用英语小数点或英语月份名称的应用程序,则需要设置LC_ALL = C:

    $ printf '%g\n' 1e-2
    0,01
    $ LC_ALL=C printf '%g\n' 1e-2
    0.01
    $ date +%b
    août
    $ LC_ALL=C date +%b
    Aug
    

    这也适用于不区分大小写的比较(如grep -i)和大小写转换(awktoupper()dd conv=ucase...)。例如:

    grep -i i
    

    不能保证I在用户的语言环境中匹配。在比如一些土耳其语言环境,它没有为大写的iİ(注意点)有和小写Iı(注意漏点)。


¹根据文本的编码,这不一定是正确的选择。这对于UTF-8或单字节字符集(例如iso-8859-1)有效,但不一定非UTF-8多字节字符集。

例如,如果您在zh_HK.big5hkscs语言环境中(香港,使用BIG5中文字符编码的香港变体),并且想要在以该字符集编码的文件中查找英文字母,请执行以下任一操作:

LC_ALL=C grep '[[:alpha:]]'

要么

LC_ALL=C grep '[a-zA-Z]'

会是错误的,因为在该字符集中(还有许多其他字符集,但自UTF-8出现以来就很少使用),许多字符包含的字节对应于A-Za-z字符的ASCII编码。例如,所有A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽(还有更多)都包含的编码A是0x96 0x41,并且A是0x41(类似于ASCII)。因此,我们LC_ALL=C grep '[a-zA-Z]'将在包含那些字符的那些行上进行匹配,因为这会误解那些字节序列。

LC_COLLATE=C grep '[A-Za-z]'

会起作用,但前提LC_ALL是没有另外设置(它将覆盖LC_COLLATE)。因此,您可能最终不得不做:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

如果要在以语言环境编码编码的文件中查找英文字母。


12
+1,这是最好的答案(用于指出覆盖等)。但缺少伊格纳西奥答案的(好)示例^^
奥利维尔·杜拉克

1
次要的选择:C仅要求语言环境支持“便携式字符集”(ASCII 0-127),并且chars> 127的行为在技术上未指定。实际上,大多数程序会将它们视为不透明数据,并按照您所描述的那样传递它们。但不是全部:特别是,如果在C语言环境中运行,Ruby可能会阻塞字节数大于127的char数据。老实说,我不知道这在技术上是否“符合标准”,但是我们已经在野外看到了
Andrew Janke 2015年

2
@AndrewJanke,是的。请注意,可移植字符集并不意味着ASCII或0-127。在奥斯汀小组邮件列表上,已经有很多关于“ C”区域设置字符集的属性是什么的讨论,并且普遍的共识(将在下一个规范中阐明)是,字符集将是单字符集,字节,并包含完整的8位范围(具有此处描述的属性)。同时,是的,可能会有一些差异(例如错误或由于规范不够明确)。无论如何,LC_ALL = C是最接近您理智的行为。
斯特凡Chazelas

1
UTF-8中的Unicode代码点最多可包含4个八位位组(或字节),但是某些字符需要一个以上的代码位,这可能导致比6个八位位组更长的序列。
12431234123412341234123 '17年

1
@ 124312341234123412341234,原始的UTF-8编码最多可覆盖U + 7FFFFFFF(6个字节,有些扩展名最多可扩展到13个字节,如perl\x{7FFFFFFFFFFFFFFF}),而Unicode代码点的范围已被任意限制为U + 10FFFF (由于UTF-16设计限制),某些工具仍可以识别/产生6个字节的字符。这就是6​​字节字符的意思。在Unix语义中,一个字符是一个代码点。您的多个代码点“字符”通常被称为字素簇,以消除字符歧义。
斯特凡Chazelas

7

C是默认语言环境,“ POSIX”是“ C”的别名。我猜“ C”是从ANSI-C派生的。也许ANSI-C定义了“ POSIX”语言环境。


C和迄今为止UNIX早ANSI C.
一个CVN

@MichaelKjörling:是吗?我看过ANSI以前的文档,但没有语言环境。在AT&T贝尔实验室内部,每个人都说英语。
MSalters 2013年

@MSalters C语言的ANSI之前的文档没有提及语言环境的事实(这可能或可能不表示ANSI之前的C语言没有语言环境的概念;毕竟,我敢肯定,该语言仍然没有,但这并不重要)并不意味着C语言环境名称是从“ ANSI C”派生的。
CVn

2
@MichaelKjörling:您错过了重点。引入语言环境时,“ C”已经表示“ ANSI C”。这意味着过去的K&R C是无关紧要的。
MSalters

3

据我所知,OS X在UTF-8语言环境中使用代码点排序规则,因此,这是StéphaneChazelas答案中提到的某些点的例外。

这在OS X中打印26,在Ubuntu中打印310:

export LC_ALL=en_US.UTF-8
printf %b $(printf '\\U%08x\\n' $(seq $((0x11)) $((0x10ffff))))|grep -a '[a-z]'|wc -l

下面的代码在OS X中不显示任何内容,表示输入已排序。删除的六个代理字符会导致非法字节序列错误。

export LC_ALL=en_US.UTF-8
for ((i=1;i<=0x1fffff;i++));do
  x=$(printf %04x $i)
  [[ $x = @(000a|d800|db7f|db80|dbff|dc00|dfff) ]]&&continue
  printf %b \\U$x\\n
done|sort -c

下面的代码在OS X中不打印任何内容,表示没有两个连续的代码点(至少在U + 000B和U + D7FF之间)具有相同的排序顺序。

export LC_ALL=en_US.UTF-8
for ((i=0xb;i<=0xd7fe;i++));do
  printf %b $(printf '\\U%08x\\n' $((i+1)) $i)|sort -c 2>/dev/null&&echo $i
done

(上面的示例使用%b是因为printf \\U25在zsh中导致错误。)

GNU系统中具有相同排序顺序的某些字符和字符序列在OS X中没有相同的排序顺序。这在OS X中先打印①(使用OS X sort或GNU sort),而在Ubuntu中先打印②:

export LC_ALL=en_US.UTF-8;printf %s\\n ② ①|sort

这将在OS X(使用OS X sort或GNU sort)中打印三行,而在Ubuntu中打印一行:

export LC_ALL=en_US.UTF-8;printf %b\\n \\u0d4c \\u0d57 \\u0d46\\u0d57|sort -u

有谁知道为什么会有这种区别?
1.61803

3

似乎也可以LC_COLLATE控制ls使用的“字母顺序”。美国地区的排序方式如下:

a.C
aFilename.C
aFilename.H
a.H

基本上忽略了时期。您可能更喜欢:

a.C
a.H
aFilename.C
aFilename.H

当然可以 设置LC_COLLATEC实现这一目的。请注意,在所有大写字母之后,它也会排序为小写:

A.C
A.H
AFilename.C
a.C
a.H
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.