Do czego służy C
wartość LC_ALL
w systemach uniksopodobnych?
Wiem, że wymusza to to samo ustawienie regionalne dla wszystkich aspektów, ale co robi C
?
Do czego służy C
wartość LC_ALL
w systemach uniksopodobnych?
Wiem, że wymusza to to samo ustawienie regionalne dla wszystkich aspektów, ale co robi C
?
Odpowiedzi:
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
LC_ALL
to zmienna środowiskowa, która zastępuje wszystkie pozostałe ustawienia lokalizacji ( z wyjątkiem $LANGUAGE
niektó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 $LANG
preferencje za pomocą wartości identyfikującej twój region (np. fr_CH.UTF-8
Jeśli jesteś w francuskojęzycznej Szwajcarii, używając UTF-8). Poszczególne LC_xxx
zmienne przesłaniają pewien aspekt. LC_ALL
zastępuje je wszystkie. locale
Poleceń, 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 C
jest 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 a
do z
, musisz ustawić LC_ALL=C
.
W systemach GNU LC_ALL=C
i LC_ALL=POSIX
(lub LC_MESSAGES=C|POSIX
) zastępuj $LANGUAGE
, podczas gdy LC_ALL=anything-else
nie.
Kilka przypadków, w których zwykle musisz ustawić LC_ALL=C
:
sort -u
lub sort ... | uniq...
. W wielu lokalizacjach innych niż C, w niektórych systemach (zwłaszcza GNU), niektóre znaki mają tę samą kolejność sortowania . sort -u
nie 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 C
gwarantuje ustawienie regionalne).=
operatora zgodnego z POSIX expr
lub ==
operatora zgodnego z POSIX awk
( mawk
i gawk
nie są pod tym względem POSIX), które nie sprawdzają, czy dwa ciągi są identyczne, ale czy sortują to samo.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-Z
znaki ASCII, trzeba albo LC_ALL=C grep '[[:alpha:]]'
czy LC_ALL=C grep '[a-zA-Z]'
¹. [a-z]
dopasowuje znaki, które sortują po a
i 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 bash
wzorce, może zawierać [B-Z]
lub [A-Y]
. W wielu lokalizacjach UTF-8 (w tym en_US.UTF-8
w większości systemów) [a-z]
będą zawierać litery łacińskie od a
do y
z znakami diakrytycznymi, ale nie te z
(odz
sortuje przed nimi), czego nie wyobrażam sobie, co byś chciał (dlaczego chcesz to uwzględnić, é
a nie ź
?).arytmetyka zmiennoprzecinkowa w ksh93
. ksh93
honoruje decimal_point
ustawienie 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.
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 C
ustawieniach 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ę I
w ustawieniach regionalnych użytkownika. Na przykład w niektórych tureckich ustawieniach regionalnych nie jest to tak, że wielkie litery i
są tam İ
(zwróć uwagę na kropkę), a małe litery I
to ı
(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.big5hkscs
ustawień 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 A
0x41 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_ALL
nie 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.
C
regionalne 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 C
ustawieniach narodowych. Naprawdę nie wiem, czy to technicznie „zgodne”, ale widzieliśmy to na wolności .
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.
C
to domyślne ustawienie narodowe, „POSIX” to alias „C”. Chyba „C” pochodzi od ANSI-C. Może ANSI-C zdefiniuje ustawienia regionalne „POSIX”.
C
nazwa ustawień regionalnych pochodzi od „ANSI C”.
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, %b
ponieważ printf \\U25
powoduje 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 sort
lub 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 sort
lub GNU sort
), ale jednej linii w systemie Ubuntu:
export LC_ALL=en_US.UTF-8;printf %b\\n \\u0d4c \\u0d57 \\u0d46\\u0d57|sort -u
Wygląda na to, że LC_COLLATE
kontroluje 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_COLLATE
do C
osią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
xclock
ostrzeżeniem (Missing charsets in String to FontSet conversion
), lepiej będzie, jeśli użyjesz go,LC_ALL=C.UTF-8
aby uniknąć problemów z cyrylicą. Aby ustawić tę zmienną środowiskową, musisz dodać następujący wiersz na końcu~/.bashrc
pliku -export LC_ALL=C.UTF-8