Что делает «LC_ALL = C»?


324

Какое Cзначение имеет для LC_ALLUnix-подобных систем?

Я знаю, что это требует одного и того же языка для всех аспектов, но что делает 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

Ответы:


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 для хороших примеров, но не хватает важной информации, которая есть на ответ Стефана ...
Оливье Дюлак

4
Что вы подразумеваете под языком по умолчанию ?
Стефан Шазелас

2
Да, я понимаю, что автор может делать все, что ему нравится, в том числе не делать то, что написано на банке. Дело в том. Американский английский является единственным языком, который может быть корректно представлен с помощью набора символов в LC_ALL = C, единственном языке, где порядок сортировки в LC_ALL = C (LC_COLLATE) имеет смысл, а LC_ALL = C (LC_TIME) имеет английские названия месяца и дня. Я никогда не видел приложений, в которых LC_ALL = C возвращает сообщение на другом языке, нежели LC_ALL = en LANGUAGE = en. Так я имею право сообщать об ошибке в программе, если это не так? (здесь речь не идет о приложениях, не переведенных на английский).
Стефан Шазелас

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 от ato z, вы должны установить LC_ALL=C.

В системах GNU LC_ALL=Cи LC_ALL=POSIX(или LC_MESSAGES=C|POSIX) переопределения $LANGUAGEпока LC_ALL=anything-elseне будет.

Несколько случаев, когда вам обычно нужно установить LC_ALL=C:

  • sort -uили sort ... | uniq.... Во многих локалях, отличных от C, в некоторых системах (особенно в GNU) некоторые символы имеют одинаковый порядок сортировки . sort -uне сообщает уникальные строки, но одну из каждой группы строк, имеющих одинаковый порядок сортировки. Поэтому, если вам нужны уникальные строки, вам нужен языковой стандарт, в котором символы являются байтовыми, а все символы имеют разный порядок сортировки (что Cгарантирует языковой стандарт).
  • то же самое относится к =оператору POSIX-совместимого exprили ==оператору POSIX-совместимого awk( mawkи gawkне POSIX в этом отношении), который не проверяет, идентичны ли две строки, но сортируют ли они одинаково.
  • Диапазон символов, как в grep. Если вы хотите сопоставить букву на языке пользователя, используйте grep '[[:alpha:]]'и не изменяйте LC_ALL. Но если вы хотите сопоставить a-zA-Zсимволы ASCII, вам нужно либо либо, 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что-то вроде них), что я не могу представить, было бы то, что вы хотите (почему вы хотите включить, éа не ź?).
  • арифметика с плавающей точкой в ksh93. ksh93чтит 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 (как практически все наборы символов). Например, grep '<.*>'для поиска строк, содержащих a <, >пара не будет работать, если вы находитесь в локали UTF-8, а ввод кодируется в однобайтовом 8-битном наборе символов, например iso8859-15. Это потому, что .только совпадающие символы и символы не ASCII в iso8859-15, скорее всего, не образуют действительный символ в 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) и преобразование регистра ( awk's toupper(), 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) многие символы содержат байты, соответствующие кодировке ASCII символов A-Za-z. Например, все A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽(и многие другие) содержат кодировку A. 0x96 0x41 и A0x41 как в 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), а поведение для символов> 127 технически не определено . На практике большинство программ будут обрабатывать их как непрозрачные данные и передавать их, как вы описали. Но не все: в частности, Ruby может подавить данные char с байтами> 127, если работает в Cлокали. Честно говоря, я не знаю, технически ли это «соответствует», но мы видели это в дикой природе .
Эндрю Янке

2
@AndrewJanke, да. Обратите внимание, что переносимый набор символов не подразумевает ни ASCII, ни 0-127. В списке рассылки группы Austin было много дискуссий о том, какими будут свойства набора символов локали "C", и общее мнение (и это будет разъяснено в следующей спецификации) состоит в том, что эта кодировка будет одиночной. байт и охватывают весь 8-битный диапазон (со свойствами, описанными здесь). В то же время, да, может быть некоторое расхождение (как ошибка или потому, что спецификация недостаточно ясна). В любом случае LC_ALL = C - самое близкое, что вы можете получить в разумном смысле.
Стефан Шазелас

1
Кодовая точка Unicode в UTF-8 может иметь максимум 4 октета (или байта), но для некоторых символов требуется более одной кодовой точки, что может привести к более длинным последовательностям, чем 6 октетов.
12431234123412341234123

1
@ 12431234123412341234123, оригинальная кодировка UTF-8 покрывает до U + 7FFFFFFF (6 байтов, и есть некоторые расширения, которые могут доходить до 13 байтов, как perlи \x{7FFFFFFFFFFFFFFF}), и в то время как диапазон кодовых точек Unicode был произвольно ограничен U + 10FFFF (из-за ограничений конструкции UTF-16) некоторые инструменты по-прежнему распознают / генерируют 6-байтовые символы. Вот что я имел в виду под 6-байтовыми символами. В семантике Unix один символ - это одна кодовая точка. Ваши более чем одна кодовая точка «символы» чаще всего обозначаются как графемные кластеры для устранения неоднозначности символов.
Стефан

7

Cэто локаль по умолчанию, "POSIX" это псевдоним "C". Я думаю, "C" является производным от ANSI-C. Возможно, ANSI-C определит локаль "POSIX".


И C, и UNIX намного раньше ANSI C.
CVn

@ MichaelKjörling: Так? Я видел документацию до ANSI, и там не было локалей. Внутри AT & T Bell Labs все говорили по-английски.
MSalters

@MSalters Тот факт, что в документации до языка ANSI для языка C не упоминаются языковые стандарты (что может означать или не означать, что до версии ANSI, C не имел понятия о языковых стандартах; в конце концов, я почти уверен, что язык до сих пор не , но это не относится к делу) не означает, что Cимя локали происходит от "ANSI C".
CVn

2
@ MichaelKjörling: Вы упускаете суть. Когда были введены локали, «C» уже означало «ANSI C». То, что это означало K & R C в прошлом, не имеет значения.
MSalters

3

Насколько я могу судить, OS X использует порядок сопоставления кодовых точек в локалях UTF-8, поэтому он является исключением из некоторых пунктов, упомянутых в ответе Стефана Шазеласа.

Это печатает 26 в OS X и 310 в Ubuntu:

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_COLLATEдля Cдостижения этой цели. Обратите внимание, что он также будет сортировать строчные буквы после всех заглавных букв:

A.C
A.H
AFilename.C
a.C
a.H
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.