Co robi „LC_ALL = C”?


324

Do czego służy Cwartość LC_ALLw systemach uniksopodobnych?

Wiem, że wymusza to to samo ustawienie regionalne dla wszystkich aspektów, ale co robi C?


Jeśli chcesz rozwiązać problem z xclockostrzeżeniem ( Missing charsets in String to FontSet conversion), lepiej będzie, jeśli użyjesz go, LC_ALL=C.UTF-8aby uniknąć problemów z cyrylicą. Aby ustawić tę zmienną środowiskową, musisz dodać następujący wiersz na końcu ~/.bashrcpliku -export LC_ALL=C.UTF-8
fedotsoldier

@ Fedotsoldier powinieneś prawdopodobnie zadać pytanie i udzielić odpowiedzi sam, nie sądzę, że ma to związek z pytaniem. To tylko odpowiedź na inny problem.
jcubic

Tak, masz rację, ok
fedotsoldier

Odpowiedzi:


209

Zmusza aplikacje do używania domyślnego języka danych wyjściowych:

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

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

i wymusza sortowanie bajtowe:

$ 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 za dobre przykłady, ale brakuje ważnych informacji, które znajdują się w odpowiedzi Stephane'a ...
Olivier Dulac

4
Co rozumiesz przez domyślny język ?
Stéphane Chazelas,

2
Tak, rozumiem, że autor może robić, co chce, w tym nie robić tego, co jest napisane na puszce. Rzecz w tym. Angielski amerykański jest jedynym językiem, który można poprawnie przedstawić za pomocą zestawu znaków w LC_ALL = C, jedynym języku, w którym porządek sortowania w LC_ALL = C (LC_COLLATE) ma sens, LC_ALL = C (LC_TIME) ma angielskie nazwy miesięcy i dni. Nigdy nie widziałem aplikacji, w których LC_ALL = C zwrócił komunikat w innym języku niż LC_ALL = en LANGUAGE = en. Czy mam prawo zgłosić błąd w programie, jeśli tak nie jest? (nie mówiąc o aplikacjach, które nie zostały przetłumaczone na angielski).
Stéphane Chazelas,

2
Problem polega na tym, że „angielski angielski jest jedynym językiem, który można poprawnie przedstawić za pomocą zestawu znaków w LC_ALL = C”. Jest to zwykle prawdą tylko w programach C / C ++ przy użyciu wąskich znaków, ale nawet wtedy istnieją wyjątki (ponieważ istnieje kilka języków, które używają tylko znaków i symboli znalezionych w ASCII). Zgłoszenie błędu, gdy domyślnym językiem nie jest angielski, sprawi, że będziesz wyglądać na ... bigotycznego.
Ignacio Vazquez-Abrams,

3
Zauważ, że w języku angielskim (co oznacza LANG = en_US.utf8) wiadomości mogą (i powinny) używać znaków Unicode, takich jak „”, do cytowania ciągów znaków. Natomiast w LANG = C ma tylko znaki ASCII (podwójne cudzysłowy, cudzysłowy i apostrofy).
Ángel

332

LC_ALLto zmienna środowiskowa, która zastępuje wszystkie pozostałe ustawienia lokalizacji ( z wyjątkiem $LANGUAGEniektórych okoliczności ).

Różne aspekty lokalizacji (takie jak separator tysięcy lub znak dziesiętny, zestaw znaków, kolejność sortowania, miesiąc, nazwy dni, komunikaty językowe lub aplikacji, takie jak komunikaty o błędach, symbol waluty) można ustawić za pomocą kilku zmiennych środowiskowych.

Zazwyczaj ustawiasz $LANGpreferencje za pomocą wartości identyfikującej twój region (np. fr_CH.UTF-8Jeśli jesteś w francuskojęzycznej Szwajcarii, używając UTF-8). Poszczególne LC_xxxzmienne przesłaniają pewien aspekt. LC_ALLzastępuje je wszystkie. localePoleceń, gdy wywołana bez argumentów przedstawia podsumowanie aktualnych ustawieniach.

Na przykład w systemie GNU otrzymuję:

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

Mogę zastąpić indywidualne ustawienie, na przykład:

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

Lub:

$ LC_MONETARY=fr_FR.UTF-8 locale currency_symbol
€

Lub zastąp wszystko za pomocą LC_ALL.

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

W skrypcie, jeśli chcesz wymusić określone ustawienie, ponieważ nie wiesz, jakie ustawienia wymusił użytkownik (prawdopodobnie również LC_ALL), najlepszą, najbezpieczniejszą i ogólnie jedyną opcją jest wymuszenie LC_ALL.

Lokalizacja Cjest specjalną lokalizacją, która ma być najprostszą lokalizacją. Można również powiedzieć, że podczas gdy inne ustawienia narodowe są przeznaczone dla ludzi, ustawienia regionalne C dotyczą komputerów. W ustawieniach regionalnych C znaki są jednobajtowe, zestaw znaków to ASCII (cóż, nie jest to wymagane, ale w praktyce będzie w systemach, z których większość z nas będzie mogła korzystać), kolejność sortowania jest oparta na wartościach bajtów, język jest zwykle w języku angielskim (chociaż w przypadku komunikatów aplikacji (w przeciwieństwie do nazw takich jak nazwy miesięcy lub dni lub wiadomości w bibliotekach systemowych), zależy to od autora aplikacji), a symbole walut nie są zdefiniowane.

W niektórych systemach występuje różnica w ustawieniach regionalnych POSIX, w których na przykład nie zdefiniowano kolejności sortowania znaków spoza ASCII.

Zazwyczaj uruchamiasz polecenie z LC_ALL = C, aby uniknąć ustawień użytkownika, które mogłyby zakłócać twój skrypt. Na przykład, jeśli chcesz [a-z]dopasować 26 znaków ASCII od ado z, musisz ustawić LC_ALL=C.

W systemach GNU LC_ALL=Ci LC_ALL=POSIX(lub LC_MESSAGES=C|POSIX) zastępuj $LANGUAGE, podczas gdy LC_ALL=anything-elsenie.

Kilka przypadków, w których zwykle musisz ustawić LC_ALL=C:

  • sort -ulub sort ... | uniq.... W wielu lokalizacjach innych niż C, w niektórych systemach (zwłaszcza GNU), niektóre znaki mają tę samą kolejność sortowania . sort -unie zgłasza unikalnych wierszy, ale jeden z każdej grupy wierszy o jednakowej kolejności sortowania. Jeśli więc chcesz mieć unikalne wiersze, potrzebujesz ustawień regionalnych, w których znaki są bajtami, a wszystkie znaki mają inną kolejność sortowania (co Cgwarantuje ustawienie regionalne).
  • to samo dotyczy =operatora zgodnego z POSIX exprlub ==operatora zgodnego z POSIX awk( mawki gawknie są pod tym względem POSIX), które nie sprawdzają, czy dwa ciągi są identyczne, ale czy sortują to samo.
  • Zakresy znaków jak w grep. Jeśli chcesz dopasować literę w języku użytkownika, używaj grep '[[:alpha:]]'i nie modyfikuj LC_ALL. Ale jeśli chcesz, aby dopasować a-zA-Zznaki ASCII, trzeba albo LC_ALL=C grep '[[:alpha:]]'czy LC_ALL=C grep '[a-zA-Z]'¹. [a-z]dopasowuje znaki, które sortują po ai przed z(choć w przypadku wielu interfejsów API jest to bardziej skomplikowane). W innych lokalizacjach zazwyczaj nie wiesz, co to są. Na przykład niektóre ustawienia narodowe ignorują wielkość liter podczas sortowania, więc [a-z]w niektórych interfejsach API, takich jak bashwzorce, może zawierać [B-Z]lub [A-Y]. W wielu lokalizacjach UTF-8 (w tym en_US.UTF-8w większości systemów) [a-z]będą zawierać litery łacińskie od ado yz znakami diakrytycznymi, ale nie te z(odzsortuje przed nimi), czego nie wyobrażam sobie, co byś chciał (dlaczego chcesz to uwzględnić, éa nie ź?).
  • arytmetyka zmiennoprzecinkowa w ksh93. ksh93honoruje decimal_pointustawienie w LC_NUMERIC. Jeśli napiszesz skrypt, który zawiera a=$((1.2/7)), przestanie on działać po uruchomieniu przez użytkownika, którego ustawienia regionalne mają przecinek jako separator dziesiętny:

    $ 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
    

    Potrzebujesz więc takich rzeczy jak:

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

    Na marginesie: ,separator dziesiętny ,powoduje konflikt z operatorem arytmetycznym, co może powodować jeszcze większe zamieszanie.

  • Gdy potrzebujesz znaków w bajtach. Obecnie większość ustawień regionalnych oparta jest na UTF-8, co oznacza, że ​​znaki mogą zajmować od 1 do 6 bajtów. W przypadku danych, które mają być bajtami, za pomocą narzędzi tekstowych należy ustawić LC_ALL = C. Znacząco poprawi to także wydajność, ponieważ parsowanie danych UTF-8 wiąże się z pewnymi kosztami.
  • następstwo poprzedniego punktu: podczas przetwarzania tekstu, w którym nie wiadomo, w jakim zestawie znaków zapisano dane wejściowe, ale można założyć, że jest on zgodny z ASCII (ponieważ praktycznie wszystkie zestawy znaków są). Na przykład, grep '<.*>'aby wyszukać wiersze zawierające a <, >para nie będzie działać, jeśli jesteś w ustawieniach regionalnych UTF-8, a dane wejściowe są zakodowane w jednym bajcie 8-bitowym zestawie znaków, takim jak iso8859-15. Jest tak, ponieważ .tylko znaki pasujące i znaki spoza ASCII w iso8859-15 prawdopodobnie nie tworzą prawidłowego znaku w UTF-8. Z drugiej strony LC_ALL=C grep '<.*>'zadziała, ponieważ każda wartość bajtu tworzy poprawny znak w Custawieniach regionalnych.
  • Za każdym razem, gdy przetwarzasz dane wejściowe lub wyjściowe, które nie są przeznaczone dla / dla człowieka. Jeśli rozmawiasz z użytkownikiem, możesz chcieć użyć jego konwencji i języka, ale na przykład, jeśli wygenerujesz pewne liczby, aby nakarmić inną aplikację, która oczekuje kropek dziesiętnych w stylu angielskim lub nazw angielskich miesięcy, możesz ustaw 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
    

    Dotyczy to również takich rzeczy, jak porównanie bez rozróżniania wielkości liter (jak w grep -i) i konwersja wielkości liter ( awk's toupper(), dd conv=ucase...). Na przykład:

    grep -i i
    

    nie gwarantuje się, że dopasuje się Iw ustawieniach regionalnych użytkownika. Na przykład w niektórych tureckich ustawieniach regionalnych nie jest to tak, że wielkie litery isą tam İ(zwróć uwagę na kropkę), a małe litery Ito ı(zauważ brakującą kropkę).


¹ W zależności od kodowania tekstu niekoniecznie jest to jednak właściwe. Dotyczy to zestawów znaków UTF-8 lub jednobajtowych (takich jak iso-8859-1), ale niekoniecznie zestawów znaków wielobajtowych innych niż UTF-8.

Na przykład, jeśli korzystasz z zh_HK.big5hkscsustawień regionalnych (Hongkong, korzystasz z wariantu Hongkongu chińskiego kodowania znaków BIG5) i chcesz poszukać angielskich liter w pliku zakodowanym w tych zestawach znaków, wykonując:

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

lub

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

byłoby błędne, ponieważ w tym zestawie znaków (i wielu innych, ale rzadko używanych od czasu pojawienia się UTF-8), wiele znaków zawiera bajty odpowiadające kodowaniu ASCII znaków A-Za-z. Na przykład wszystkie A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽(i wiele innych) zawierają kodowanie A. to 0x96 0x41 i A0x41 jak w ASCII. Zatem nasze LC_ALL=C grep '[a-zA-Z]'pasowałyby do tych wierszy, które zawierają te znaki, ponieważ błędnie interpretują te sekwencje bajtów.

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

działałoby, ale tylko wtedy, gdy LC_ALLnie jest inaczej ustawione (co zastąpiłoby LC_COLLATE). Więc możesz skończyć z koniecznością:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

jeśli chcesz poszukać angielskich liter w pliku zakodowanym w kodowaniu ustawień regionalnych.


12
+1, to najlepsza odpowiedź (do wskazania zastąpienia itp.). Ale brakuje (ładnych) przykładów odpowiedzi Ignacio ^^
Olivier Dulac

1
Drobny nitpick: Ustawienia Cregionalne są wymagane tylko do obsługi „przenośnego zestawu znaków” (ASCII 0-127), a zachowanie znaków> 127 jest technicznie nieokreślone . W praktyce większość programów traktuje je jako nieprzejrzyste dane i przekazuje je zgodnie z opisem. Ale nie wszystkie: w szczególności Ruby może zadławić się danymi char o bajtach> 127, jeśli działa w Custawieniach narodowych. Naprawdę nie wiem, czy to technicznie „zgodne”, ale widzieliśmy to na wolności .
Andrew Janke,

2
@AndrewJanke, tak. Należy pamiętać, że przenośny zestaw znaków nie oznacza ASCII ani 0-127. Wiele dyskusji na liście mailingowej grupy Austin dotyczyło właściwości zestawu znaków regionalnych „C”, a ogólny konsensus (który zostanie wyjaśniony w następnej specyfikacji) jest taki, że zestaw znaków byłby pojedynczy- bajt i obejmuje pełny zakres 8-bitowy (z opisanymi tutaj właściwościami). W międzyczasie tak, może wystąpić pewna rozbieżność (jako błąd lub ponieważ specyfikacja nie jest wystarczająco wyraźna). W anycase LC_ALL = C jest najbliżej rozsądnego zachowania.
Stéphane Chazelas,

1
Punkt kodowy Unicode w UTF-8 może mieć maksymalnie 4 oktety (lub bajty), ale niektóre Postacie potrzebują więcej niż jednego punktu kodowego, co może prowadzić do dłuższych sekwencji niż 6 oktetów.
12431234123412341234123 18.04.17

1
@ 12431234123412341234123 pierwotny kodowania UTF-8 pokrywa U + 7FFFFFFF (6 bajtów, a pewne rozszerzenie się go do 13 bajtów, takie jak perl„s \x{7FFFFFFFFFFFFFFF}), a gdy zakres unikodowe punktów kod został dowolnie ograniczony U + 10FFFF (z powodu ograniczeń projektowych UTF-16), niektóre narzędzia nadal rozpoznają / tworzą 6-bajtowe znaki. To właśnie miałem na myśli 6 bajtowych znaków. W semantyce uniksowej jeden znak jest jednym punktem kodowym. Twoje więcej niż jeden punkt kodowy „znaki” są bardziej ogólnie określany jako klastry graphem disambiguate z postaciami.
Stéphane Chazelas,

7

Cto domyślne ustawienie narodowe, „POSIX” to alias „C”. Chyba „C” pochodzi od ANSI-C. Może ANSI-C zdefiniuje ustawienia regionalne „POSIX”.


Zarówno C, jak i UNIX zdecydowanie wyprzedzają ANSI C.
CVn

@ MichaelKjörling: Więc? Widziałem dokumentację sprzed ANSI i nie miała ona lokalizacji. Wewnątrz AT&T Bell Labs wszyscy mówili po angielsku.
MSalters

@MSalters Fakt, że dokumentacja przed ANSI dla języka C nie wspomina o ustawieniach regionalnych (co może, ale nie musi sugerować, że pre-ANSI, C nie miał pojęcia lokalizacji; w końcu jestem prawie pewien, że język nadal nie , ale to nie ma znaczenia) nie oznacza, że Cnazwa ustawień regionalnych pochodzi od „ANSI C”.
CVn

2
@ MichaelKjörling: Nie rozumiesz sedna sprawy. Kiedy wprowadzono ustawienia narodowe, „C” oznaczało już „ANSI C”. To, że oznaczało to, że K&R C w przeszłości jest nieistotne.
MSalters

3

O ile mogę stwierdzić, OS X używa porządku sortowania punktów kodowych w ustawieniach regionalnych UTF-8, więc jest to wyjątek od niektórych punktów wymienionych w odpowiedzi Stéphane Chazelas.

Wypisuje 26 w OS X i 310 w Ubuntu:

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

Poniższy kod nie drukuje nic w OS X, co wskazuje, że dane wejściowe są posortowane. Sześć usuniętych znaków zastępczych powoduje błąd niedozwolonej sekwencji bajtów.

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

Poniższy kod nie drukuje nic w OS X, co wskazuje, że nie ma dwóch kolejnych punktów kodu (przynajmniej między U + 000B i U + D7FF), które mają tę samą kolejność sortowania.

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

(W powyższych przykładach użyto, %bponieważ printf \\U25powoduje błąd w zsh.)

Niektóre znaki i sekwencje znaków, które mają tę samą kolejność sortowania w systemach GNU, nie mają tej samej kolejności sortowania w OS X. Spowoduje to wydrukowanie ① najpierw w OS X (używając OS X sortlub GNU sort), ale ② najpierw w Ubuntu:

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

Spowoduje to wydrukowanie trzech linii w systemie OS X (przy użyciu systemu OS X sortlub GNU sort), ale jednej linii w systemie Ubuntu:

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

Czy ktoś wie, dlaczego jest taka różnica?
1.61803

3

Wygląda na to, że LC_COLLATEkontroluje również „kolejność alfabetyczną” używaną przez ls. Ustawienia regionalne w USA będą sortowane w następujący sposób:

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

w zasadzie ignorując kropki. Może wolisz:

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

Na pewno tak. Ustawienie LC_COLLATEdo Cosiągnięcia tego. Zauważ, że sortuje również małe litery po wszystkich wielkich literach:

A.C
A.H
AFilename.C
a.C
a.H
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.