Que fait “LC_ALL = C”?


324

Quelle est la Cvaleur pour LC_ALLles systèmes de type Unix?

Je sais que cela force le même lieu pour tous les aspects mais que fait C-on?


Si vous souhaitez résoudre un problème avec xclockwarning ( Missing charsets in String to FontSet conversion), il sera préférable LC_ALL=C.UTF-8d’éviter les problèmes avec cyrillic. Pour définir cette variable d’environnement, vous devez ajouter la ligne suivante à la fin du ~/.bashrcfichier -export LC_ALL=C.UTF-8
fedotsoldier

@ fedotsoldier, vous devriez probablement poser une question et donner la réponse vous-même, je ne pense pas que cela soit lié à la question. C'est juste répondre à un problème différent que vous rencontrez.
jcubic

Ouais, tu as raison, d'accord
fedotsoldier

Réponses:


209

Il oblige les applications à utiliser la langue par défaut pour la sortie:

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

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

et les forces de tri pour être en octets:

$ 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 pour de bons exemples, mais il manque les informations importantes qui se trouvent sur la réponse de Stéphane ...
Olivier Dulac

4
Qu'entendez-vous par langue par défaut ?
Stéphane Chazelas

2
Oui, je comprends que l'auteur peut faire ce qu'il veut, y compris ne pas faire ce qu'il dit sur l'étain. La chose est. L'anglais américain est la seule langue pouvant être représentée correctement avec le jeu de caractères LC_ALL = C, la seule langue dans laquelle l'ordre de tri LC_ALL = C (LC_COLLATE) est logique, LC_ALL = C (LC_TIME) a ​​des noms de mois et de jours en anglais. Je n'ai jamais vu d'applications dans lesquelles LC_ALL = C a renvoyé un message dans une langue différente de LC_ALL = en LANGUAGE = en. Alors, ai-je le droit de signaler un bogue contre un programme si ce n'est pas le cas? (ne parle pas d'applications non traduites en anglais ici).
Stéphane Chazelas

2
Le problème est "L'anglais américain est la seule langue pouvant être représentée correctement avec le jeu de caractères dans LC_ALL = C". Cela n’est généralement vrai que dans les programmes C / C ++ lorsqu’on utilise des caractères étroits, mais il existe néanmoins des exceptions (puisqu’il existe plusieurs langues qui utilisent uniquement des caractères et des symboles trouvés en ASCII). Signaler un bogue lorsque la langue par défaut n'est pas l'anglais vous fera paraître ... fanatique.
Ignacio Vazquez-Abrams

3
Notez qu'en anglais (ce qui signifie LANG = en_US.utf8), les messages peuvent (et devraient) utiliser des caractères Unicode tels que «» pour citer des chaînes. Alors que dans LANG = C, il n’en a que des ASCII (guillemets, guillemets et apostrophes).
Ángel

332

LC_ALLest la variable d'environnement qui remplace tous les autres paramètres de localisation ( sauf $LANGUAGEdans certaines circonstances ).

Différents aspects des localisations (comme le séparateur de milliers ou le point, le jeu de caractères, l'ordre de tri, les noms de mois et de jour, la langue ou les messages d'application tels que les messages d'erreur, le symbole monétaire) peuvent être définis à l'aide de quelques variables d'environnement.

Vous définissez généralement $LANGvos préférences avec une valeur identifiant votre région (par exemple, fr_CH.UTF-8si vous êtes en Suisse romande et utilisez UTF-8). Les LC_xxxvariables individuelles remplacent un certain aspect. LC_ALLles remplace tous. La localecommande, lorsqu'elle est appelée sans argument, donne un résumé des paramètres actuels.

Par exemple, sur un système GNU, je reçois:

$ 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=

Je peux remplacer un paramètre individuel avec par exemple:

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

Ou:

$ LC_MONETARY=fr_FR.UTF-8 locale currency_symbol
€

Ou remplacez tout avec LC_ALL.

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

Dans un script, si vous souhaitez forcer un paramètre spécifique, car vous ne savez pas quels paramètres l'utilisateur a forcé (éventuellement également LC_ALL), votre meilleure option, généralement la plus sûre et la plus courante, consiste à forcer LC_ALL.

La Clocale est une locale spéciale censée être la locale la plus simple. Vous pouvez également dire que, tandis que les autres paramètres régionaux sont destinés aux humains, les paramètres régionaux C sont destinés aux ordinateurs. Dans les paramètres régionaux C, les caractères sont des octets simples, le jeu de caractères est ASCII (eh bien, ce n’est pas une obligation, mais sera en pratique dans les systèmes que la plupart d’entre nous ne pourront jamais utiliser), l’ordre de tri est basé sur les valeurs des octets, la langue est généralement l'anglais américain (bien que pour les messages d'application (par opposition à des noms tels que les noms de mois ou de jours ou les messages des bibliothèques système), c'est à la discrétion de l'auteur de l'application) et des éléments tels que les symboles monétaires ne sont pas définis.

Sur certains systèmes, il existe une différence avec les paramètres régionaux POSIX où, par exemple, l'ordre de tri des caractères non-ASCII n'est pas défini.

Vous exécutez généralement une commande avec LC_ALL = C pour éviter que les paramètres de l'utilisateur n'interfèrent avec votre script. Par exemple, si vous souhaitez [a-z]faire correspondre les 26 caractères ASCII de aà z, vous devez définir LC_ALL=C.

Sur les systèmes GNU, LC_ALL=Cet LC_ALL=POSIX(ou LC_MESSAGES=C|POSIX) remplacer $LANGUAGE, alors LC_ALL=anything-elseque non.

Quelques cas où vous devez généralement définir LC_ALL=C:

  • sort -uou sort ... | uniq.... Dans de nombreux environnements locaux autres que C, sur certains systèmes (notamment ceux de GNU), certains caractères ont le même ordre de tri . sort -une rapporte pas de lignes uniques, mais une de chaque groupe de lignes ayant un ordre de tri égal. Donc, si vous voulez des lignes uniques, vous avez besoin d'une locale où les caractères sont exprimés en octets et où tous les caractères ont un ordre de tri différent (ce que la Clocale garantit).
  • Il en va de même pour l' =opérateur de POSIX conforme exprou celui ==de POSIX conforme awk( mawket gawkne sont pas POSIX à cet égard), qui ne vérifie pas si deux chaînes sont identiques, mais si elles se trient de la même manière.
  • Les plages de caractères comme dans grep. Si vous voulez faire correspondre une lettre dans la langue de l'utilisateur, utilisez grep '[[:alpha:]]'et ne modifiez pas LC_ALL. Mais si vous souhaitez faire correspondre les a-zA-Zcaractères ASCII, vous avez besoin de LC_ALL=C grep '[[:alpha:]]'ou LC_ALL=C grep '[a-zA-Z]'¹. [a-z]correspond aux caractères triés aavant et après z(bien qu'avec de nombreuses API, c'est plus compliqué que cela). Dans d'autres endroits, vous ne savez généralement pas ce que c'est. Par exemple, certains paramètres régionaux ignorent la casse pour le tri. Par conséquent, [a-z]dans certaines API telles que les bashmodèles, ils peuvent inclure [B-Z]ou [A-Y]. Dans beaucoup de locales UTF-8 (y compris en_US.UTF-8sur la plupart des systèmes), [a-z]inclura les lettres latines de aà yavec des signes diacritiques mais pas celles de z(depuiszque je ne peux pas imaginer serait ce que vous voulez (pourquoi voudriez-vous inclure éet non ź?).
  • arithmétique en virgule flottante ksh93. ksh93honore la decimal_pointmise en LC_NUMERIC. Si vous écrivez un script qui contient a=$((1.2/7)), il ne fonctionnera plus s'il est exécuté par un utilisateur dont les paramètres régionaux ont une virgule comme séparateur décimal:

    $ 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
    

    Ensuite, vous avez besoin de choses comme:

    #! /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
    

    En remarque: le ,séparateur décimal est en conflit avec l' ,opérateur arithmétique, ce qui peut causer encore plus de confusion.

  • Lorsque vous avez besoin que les caractères soient des octets. De nos jours, la plupart des locales sont basées sur UTF-8, ce qui signifie que les caractères peuvent prendre entre 1 et 6 octets. Lorsque vous traitez avec des données censées être des octets, avec des utilitaires de texte, vous voudrez définir LC_ALL = C. Cela améliorera également considérablement les performances, car l'analyse des données UTF-8 a un coût.
  • un corollaire du point précédent: lors du traitement de texte où vous ne savez pas dans quel jeu de caractères l'entrée est écrite, mais peut supposer qu'il est compatible avec l'ASCII (comme pratiquement tous les jeux de caractères le sont). Par exemple grep '<.*>'pour rechercher les lignes contenant une <, >deux ne fonctionnera si vous êtes dans un lieu UTF-8 et l'entrée est codée dans un seul octet caractère 8 bits , comme iso8859-15. En effet, .seules les correspondances et les caractères non-ASCII de l'iso8859-15 risquent de ne pas former un caractère valide dans UTF-8. D'autre part, LC_ALL=C grep '<.*>'cela fonctionnera car toute valeur d'octet forme un caractère valide dans les Cparamètres régionaux.
  • Chaque fois que vous traitez des données d'entrée ou des données de sortie non destinées à / par un humain. Si vous parlez à un utilisateur, vous voudrez peut-être utiliser sa convention et sa langue, mais par exemple, si vous générez des nombres pour alimenter une autre application qui attend des points décimaux de style anglais, ou des noms de mois en anglais, vous souhaiterez: set 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
    

    Cela s'applique également à des choses comme la comparaison sans distinction de casse (comme dans grep -i) et la conversion de casse ( awk's toupper(), dd conv=ucase...). Par exemple:

    grep -i i
    

    ne correspond pas forcément aux Iparamètres régionaux de l'utilisateur. Dans certains endroits turcs par exemple, elle ne précise pas en majuscules iest İ(notez le point) et il en minuscules Iest ı(notez le point manquant).


¹ En fonction du codage du texte, ce n’est pas nécessairement la bonne chose à faire. Ceci est valable pour les jeux de caractères UTF-8 ou mono-octets (comme l'iso-8859-1), mais pas nécessairement pour les jeux de caractères multi-octets non UTF-8.

Par exemple, si vous vous trouvez dans un zh_HK.big5hkscsenvironnement local (Hong Kong, en utilisant la variante hongkongaise du codage de caractères chinois BIG5) et que vous souhaitez rechercher des lettres anglaises dans un fichier codé dans ce jeu de caractères, effectuez l'une des opérations suivantes:

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

ou

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

aurait tort, car dans ce jeu de caractères (et de nombreux autres, mais à peine utilisés depuis l'apparition de UTF-8), de nombreux caractères contiennent des octets correspondant au codage ASCII des caractères A-Za-z. Par exemple, tous A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽(et beaucoup d’autres) contiennent le codage de A. est 0x96 0x41 et Aest 0x41 comme en ASCII. Donc, nos LC_ALL=C grep '[a-zA-Z]'correspondraient sur les lignes contenant ces caractères, car cela interpréterait mal ces séquences d'octets.

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

fonctionnerait, mais seulement si LC_ALLn'est pas défini autrement (ce qui écraserait LC_COLLATE). Donc, vous pourriez avoir à faire:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

si vous voulez rechercher des lettres anglaises dans un fichier codé dans le codage des paramètres régionaux.


12
+1, c'est la meilleure réponse (pour signaler le dépassement, etc.). Mais il manque les (gentils) exemples de la réponse d'Ignacio ^^
Olivier Dulac

1
Un nitpick mineur: les Cparamètres régionaux sont requis uniquement pour prendre en charge le "jeu de caractères portable" (ASCII 0-127), et le comportement des caractères supérieurs à 127 est techniquement non spécifié . En pratique, la plupart des programmes les traiteront comme des données opaques et les transmettront comme vous l'avez décrit. Mais pas tous: en particulier, Ruby peut s’étouffer avec des données char avec des octets> 127 s’il s’exécute dans les Cparamètres régionaux. Honnêtement, je ne sais pas si c'est techniquement "conforme", mais nous l'avons vu à l'état sauvage .
Andrew Janke

2
@ AndrewJanke, oui. Notez que le jeu de caractères portable n'implique pas les événements ASCII ni 0-127. Il y a eu beaucoup de discussions sur la liste de diffusion du groupe d'Austin sur les propriétés du jeu de caractères régional "C" et le consensus général (et cela sera précisé dans la prochaine spécification) est que ce jeu de caractères serait unique octet et englobe la plage complète de 8 bits (avec les propriétés décrites ici). En attendant, oui, il peut y avoir une certaine divergence (comme un bogue ou parce que la spécification n’est pas assez explicite). Dans tous les cas, LC_ALL = C est le plus proche, vous pouvez obtenir un comportement sain d'esprit.
Stéphane Chazelas

1
Un point de code Unicode dans UTF-8 peut avoir un maximum de 4 octets (ou octets), mais certains caractères nécessitent plus d'un point de code, ce qui peut entraîner des séquences plus longues que 6 octets.
12431234123412341234123

1
@ 12431234123412341234123, le codage UTF-8 d' origine couvre jusqu'à U + 7FFFFFFF (6 octets, et il y a des extensions pour aller jusqu'à 13 octets tels que perl« s \x{7FFFFFFFFFFFFFFF}) et tandis que la gamme de points de code Unicode a été arbitrairement limitée à U + 10FFFF (en raison de la limitation de conception UTF-16), certains outils reconnaissent / produisent toujours des caractères de 6 octets. C'est ce que je voulais dire par 6 caractères sur 12 octets. Dans la sémantique Unix, un caractère est un point de code. Vos plus d'un codepoint « caractères » sont plus généralement référencés comme des grappes de GRAPHEM désambiguïser de caractères.
Stéphane Chazelas

7

Cest la langue par défaut, "POSIX" est l'alias de "C". Je suppose que "C" est dérivé de ANSI-C. Peut-être que ANSI-C définit les paramètres régionaux "POSIX".


Les deux C et UNIX sont de loin antérieurs à ANSI C.
CVn

@ MichaelKjörling: Alors? J'ai vu la documentation pré-ANSI et elle n'avait pas de paramètres régionaux. En interne, chez AT & T Bell Labs, tout le monde parlait anglais.
MSalters

@MSalters Le fait que la documentation pré-ANSI pour le langage C ne mentionne pas les environnements locaux (ce qui peut impliquer ou non que pré-ANSI, C n'avait pas de concept de paramètres régionaux; après tout, je suis à peu près sûr que le langage ne le fait toujours pas. , mais c’est à côté de cela) n’implique pas que le Cnom de la locale dérive de "ANSI C".
un CVn

2
@ MichaelKjörling: Vous manquez le point. Lorsque les paramètres régionaux ont été introduits, "C" signifiait déjà "ANSI C". Que cela signifiait K & R C dans le passé est hors de propos.
MSalters

3

Autant que je sache, OS X utilise l'ordre de classement des points de code dans les paramètres régionaux UTF-8. Il s'agit donc d'une exception à certains points mentionnés dans la réponse de Stéphane Chazelas.

Cela imprime 26 sous OS X et 310 sous Ubuntu:

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

Le code ci-dessous n’imprime rien dans OS X, ce qui indique que l’entrée est triée. Les six caractères de substitution qui sont supprimés provoquent une erreur de séquence d'octets non conforme.

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

Le code ci-dessous n'imprime rien dans OS X, indiquant qu'il n'y a pas deux points de code consécutifs (au moins entre U + 000B et U + D7FF) qui ont le même ordre de classement.

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

(Les exemples ci-dessus utilisent %bcar il en printf \\U25résulte une erreur dans zsh.)

Certains caractères et séquences de caractères qui ont le même ordre de classement dans les systèmes GNU n'ont pas le même ordre de classement dans OS X. Ceci affiche ① en premier dans OS X (avec OS X sortou GNU sort), mais en d'abord dans Ubuntu:

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

Ceci affiche trois lignes sous OS X (avec OS X sortou GNU sort) mais une ligne sous Ubuntu:

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

Est-ce que quelqu'un sait pourquoi il y a cette différence?
1,61803

3

Il semble que cela LC_COLLATEcontrôle "l'ordre alphabétique" utilisé par ls. Les paramètres régionaux américains seront triés comme suit:

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

en ignorant fondamentalement les périodes. Vous pourriez préférer:

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

Je fais certainement. Paramétrer LC_COLLATEpour Caccomplir ceci. Notez qu'il va également trier les minuscules après toutes les capitales:

A.C
A.H
AFilename.C
a.C
a.H
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.