Apa yang dilakukan “LC_ALL = C”?

324

Apa Cnilai untuk LC_ALLdilakukan dalam sistem seperti Unix?

Saya tahu itu memaksa lokal yang sama untuk semua aspek tapi apa fungsinya C?

jcubic
sumber
Jika Anda ingin menyelesaikan masalah dengan xclockperingatan ( Missing charsets in String to FontSet conversion), akan lebih baik jika Anda akan menggunakan LC_ALL=C.UTF-8untuk menghindari masalah dengan cyrillic. Untuk mengatur variabel lingkungan ini, Anda harus menambahkan baris berikut di akhir ~/.bashrcfile -export LC_ALL=C.UTF-8
fedotsoldier
@fedotsoldier Anda mungkin harus bertanya dan memberikan jawabannya sendiri, saya tidak berpikir itu terkait dengan pertanyaan. Itu hanya jawaban untuk masalah berbeda yang Anda alami.
jcubic
Ya, Anda benar, ok
fedotsoldier

Jawaban:

209

Ini memaksa aplikasi untuk menggunakan bahasa default untuk output:

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

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

dan memaksa sortir menjadi byte-wise:

$ 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
Ignacio Vazquez-Abrams
sumber
20
+1 untuk contoh yang baik, tetapi tidak memiliki info penting yang ada pada jawaban Stephane ...
Olivier Dulac
4
Apa yang Anda maksud dengan bahasa default ?
Stéphane Chazelas
2
Ya, saya mengerti penulis dapat melakukan apapun yang dia suka termasuk tidak melakukan apa yang tertulis di kaleng. Permasalahannya adalah. Bahasa Inggris AS adalah satu-satunya bahasa yang dapat diwakili dengan benar dengan charset di LC_ALL = C, satu-satunya bahasa di mana urutan pengurutan di LC_ALL = C (LC_COLLATE) masuk akal, LC_ALL = C (LC_TIME) memiliki nama bulan dan hari dalam bahasa Inggris. Saya belum pernah melihat aplikasi di mana LC_ALL = C mengembalikan pesan dalam bahasa yang berbeda dari LC_ALL = en LANGUAGE = en. Jadi apakah saya berhak melaporkan bug terhadap suatu program jika bukan itu masalahnya? (tidak berbicara tentang aplikasi yang tidak diterjemahkan ke bahasa Inggris di sini).
Stéphane Chazelas
2
Masalahnya adalah "Bahasa Inggris AS adalah satu-satunya bahasa yang dapat diwakili dengan benar dengan charset di LC_ALL = C". Ini biasanya hanya benar dalam program C / C ++ ketika menggunakan karakter yang sempit, tetapi meskipun demikian ada pengecualian (karena ada beberapa bahasa yang hanya menggunakan karakter dan simbol yang ditemukan di ASCII). Melaporkan bug ketika bahasa default bukan bahasa Inggris akan membuat Anda tampak ... fanatik.
Ignacio Vazquez-Abrams
3
Perhatikan bahwa dalam bahasa Inggris (artinya LANG = en_US.utf8) pesan dapat (dan harus) menggunakan karakter unicode seperti "" untuk mengutip string. Sedangkan dalam LANG = C, itu hanya memiliki ASCII (tanda kutip ganda, tanda kutip dan apostrof).
Ángel
332

LC_ALLadalah variabel lingkungan yang menimpa semua pengaturan lokalisasi lainnya ( kecuali $LANGUAGEdalam beberapa keadaan ).

Berbagai aspek lokalisasi (seperti seribu pemisah atau karakter titik desimal, set karakter, urutan penyortiran, bulan, nama hari, bahasa atau pesan aplikasi seperti pesan kesalahan, simbol mata uang) dapat diatur menggunakan beberapa variabel lingkungan.

Anda biasanya akan menetapkan $LANGpreferensi Anda dengan nilai yang mengidentifikasi wilayah Anda (seperti fr_CH.UTF-8jika Anda berada di Swiss berbahasa Perancis, menggunakan UTF-8). LC_xxxVariabel individual mengesampingkan aspek tertentu. LC_ALLmenimpa mereka semua. The localeperintah, saat dipanggil tanpa argumen memberikan ringkasan dari pengaturan saat ini.

Misalnya, pada sistem GNU, saya mendapatkan:

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

Saya dapat mengganti pengaturan individual dengan misalnya:

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

Atau:

$ LC_MONETARY=fr_FR.UTF-8 locale currency_symbol
€

Atau menimpa segalanya dengan LC_ALL.

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

Dalam skrip, jika Anda ingin memaksakan pengaturan tertentu, karena Anda tidak tahu pengaturan apa yang dipaksakan pengguna (mungkin juga LC_ALL), opsi terbaik, teraman dan umumnya satu-satunya adalah memaksa LC_ALL.

The Clokal adalah lokal khusus yang dimaksudkan untuk menjadi lokal yang paling sederhana. Anda juga bisa mengatakan bahwa sementara lokal lainnya untuk manusia, lokal C adalah untuk komputer. Dalam C locale, karakter adalah byte tunggal, charsetnya adalah ASCII (well, tidak diharuskan, tetapi dalam praktiknya akan ada di sistem yang sebagian besar dari kita akan pernah menggunakan), urutan penyortiran didasarkan pada nilai byte, bahasa biasanya US English (meskipun untuk pesan aplikasi (yang bertentangan dengan hal-hal seperti nama bulan atau hari atau pesan oleh pustaka sistem), itu berdasarkan kebijaksanaan penulis aplikasi) dan hal-hal seperti simbol mata uang tidak didefinisikan.

Pada beberapa sistem, ada perbedaan dengan lokal POSIX di mana misalnya urutan sortir untuk karakter non-ASCII tidak ditentukan.

Anda biasanya menjalankan perintah dengan LC_ALL = C untuk menghindari pengaturan pengguna untuk mengganggu skrip Anda. Misalnya, jika Anda ingin [a-z]mencocokkan 26 karakter ASCII dari ahingga z, Anda harus mengatur LC_ALL=C.

Pada sistem GNU, LC_ALL=Cdan LC_ALL=POSIX(atau LC_MESSAGES=C|POSIX) menimpa $LANGUAGE, sementara LC_ALL=anything-elsetidak.

Beberapa kasus di mana Anda biasanya perlu mengatur LC_ALL=C:

  • sort -uatau sort ... | uniq.... Di banyak lokal selain C, pada beberapa sistem (terutama yang GNU), beberapa karakter memiliki urutan penyortiran yang sama . sort -utidak melaporkan garis unik, tetapi satu dari setiap kelompok garis yang memiliki urutan penyortiran yang sama. Jadi jika Anda menginginkan garis yang unik, Anda memerlukan lokal tempat karakter byte dan semua karakter memiliki urutan penyortiran yang berbeda (yang Cdijamin lokal).
  • hal yang sama berlaku untuk =operator yang patuh POSIX expratau ==operator yang patuh POSIX awk( mawkdan gawkbukan POSIX dalam hal itu), yang tidak memeriksa apakah dua string identik tetapi apakah mereka mengurutkan sama.
  • Rentang karakter seperti di grep. Jika Anda bermaksud mencocokkan huruf dalam bahasa pengguna, gunakan grep '[[:alpha:]]'dan jangan modifikasi LC_ALL. Tetapi jika Anda ingin mencocokkan a-zA-Zkarakter ASCII, Anda perlu salah satu LC_ALL=C grep '[[:alpha:]]'atau LC_ALL=C grep '[a-zA-Z]'¹. [a-z]cocok dengan karakter yang mengurutkan setelah adan sebelumnya z(meskipun dengan banyak API itu lebih rumit dari itu). Di tempat lain, Anda biasanya tidak tahu apa itu. Misalnya beberapa lokal mengabaikan kasus untuk mengurutkan sehingga [a-z]dalam beberapa API seperti bashpola, dapat menyertakan [B-Z]atau [A-Y]. Di banyak tempat UTF-8 (termasuk en_US.UTF-8pada sebagian besar sistem), [a-z]akan menyertakan huruf latin dari ahingga ydengan diakritik tetapi bukan huruf-huruf dari z(karenazmacam sebelum mereka) yang saya tidak bisa bayangkan akan menjadi apa yang Anda inginkan (mengapa Anda ingin memasukkan édan tidak ź?).
  • aritmatika floating point di ksh93. ksh93menghormati decimal_pointpengaturan dalam LC_NUMERIC. Jika Anda menulis skrip yang berisi a=$((1.2/7)), skrip tersebut akan berhenti berfungsi ketika dijalankan oleh pengguna yang lokalnya memiliki koma sebagai pemisah desimal:

    $ 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
    

    Maka Anda membutuhkan hal-hal seperti:

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

    Sebagai catatan: ,pemisah desimal bertentangan dengan ,operator aritmatika yang dapat menyebabkan lebih banyak kebingungan.

  • Ketika Anda membutuhkan karakter untuk menjadi byte. Saat ini, sebagian besar lokal berbasis UTF-8 yang berarti karakter dapat memakan waktu dari 1 hingga 6 byte. Saat berurusan dengan data yang dimaksudkan sebagai byte, dengan utilitas teks, Anda ingin mengatur LC_ALL = C. Ini juga akan meningkatkan kinerja secara signifikan karena parsing data UTF-8 memiliki biaya.
  • akibat wajar dari poin sebelumnya: saat memproses teks di mana Anda tidak tahu karakter apa yang mengatur input ditulis, tetapi dapat menganggap itu kompatibel dengan ASCII (karena hampir semua charset). Misalnya grep '<.*>'untuk mencari baris yang mengandung <, >pasangan tidak akan berfungsi jika Anda berada di lokal UTF-8 dan input dikodekan dalam set karakter 8-bit byte tunggal seperti iso8859-15. Itu karena .hanya karakter yang cocok dan karakter non-ASCII di iso8859-15 yang cenderung tidak membentuk karakter yang valid di UTF-8. Di sisi lain, LC_ALL=C grep '<.*>'akan berfungsi karena nilai byte apa pun membentuk karakter yang valid di Clokal.
  • Kapan saja di mana Anda memproses data input atau data output yang tidak dimaksudkan dari / untuk manusia. Jika Anda berbicara dengan pengguna, Anda mungkin ingin menggunakan konvensi dan bahasa mereka, tetapi misalnya, jika Anda menghasilkan beberapa angka untuk memberi makan beberapa aplikasi lain yang mengharapkan titik desimal gaya Inggris, atau nama bulan bahasa Inggris, Anda ingin atur 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
    

    Itu juga berlaku untuk hal-hal seperti perbandingan kasus tidak sensitif (seperti dalam grep -i) dan konversi kasus ( awk's toupper(), dd conv=ucase...). Misalnya:

    grep -i i
    

    tidak dijamin cocok Idengan di lokal pengguna. Di beberapa lokal Turki misalnya, tidak seperti huruf besar iadalah İ(perhatikan titik) di sana dan lebih rendah-kasus Iadalah ı(perhatikan hilang dot).


¹ Bergantung pada pengodean teks, itu belum tentu hal yang benar untuk dilakukan. Itu berlaku untuk set karakter UTF-8 atau byte tunggal (seperti iso-8859-1), tetapi tidak harus set karakter multibyte non-UTF-8.

Misalnya, jika Anda berada di zh_HK.big5hkscslokal (Hong Kong, menggunakan varian Hong Kong dari pengkodean karakter Cina BIG5), dan Anda ingin mencari huruf bahasa Inggris di file yang dikodekan dalam rangkaian karakter itu, lakukan salah satu dari:

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

atau

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

akan salah, karena dalam charset itu (dan banyak lainnya, tetapi hampir tidak digunakan sejak UTF-8 keluar), banyak karakter berisi byte yang sesuai dengan pengkodean ASCII dari karakter A-Za-z. Misalnya, semua A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽(dan banyak lagi) mengandung penyandian dari A. adalah 0x96 0x41, dan A0x41 seperti di ASCII. Jadi kami LC_ALL=C grep '[a-zA-Z]'akan mencocokkan pada baris-baris yang berisi karakter-karakter itu karena akan salah menafsirkan urutan byte tersebut.

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

akan bekerja, tetapi hanya jika LC_ALLtidak ditentukan (yang akan menimpa LC_COLLATE). Jadi Anda akhirnya harus melakukan:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

jika Anda ingin mencari huruf bahasa Inggris di file yang dikodekan dalam pengkodean lokal.

Stéphane Chazelas
sumber
12
+1, ini adalah jawaban terbaik (untuk menunjukkan penimpaan, dll). Tetapi tidak memiliki contoh (baik) dari jawaban Ignacio ^^
Olivier Dulac
1
Nitpick minor: CLokal hanya diperlukan untuk mendukung "set karakter portabel" (ASCII 0-127), dan perilaku untuk karakter> 127 secara teknis tidak ditentukan . Dalam praktiknya, sebagian besar program akan memperlakukannya sebagai data buram dan meneruskannya seperti yang Anda gambarkan. Tetapi tidak semua: khususnya, Ruby dapat mencekik data char dengan byte> 127 jika berjalan di Clokal. Jujur saya tidak tahu apakah itu secara teknis "sesuai", tapi kami sudah melihatnya di alam liar .
Andrew Janke
2
@AndrewJanke, ya. Perhatikan bahwa rangkaian karakter portabel tidak menunjukkan ASCII atau 0-127. Ada banyak diskusi di milis grup Austin mengenai sifat-sifat set karakter lokal "C" nantinya dan konsensus umum (dan yang akan diklarifikasi dalam spesifikasi berikutnya) adalah bahwa charset itu akan tunggal. byte, dan mencakup rentang 8bit penuh (dengan properti yang dijelaskan di sini). Sementara itu, ya mungkin ada beberapa perbedaan (sebagai bug atau karena speknya tidak cukup eksplisit). Dalam tas apa pun LC_ALL = C adalah yang terdekat Anda bisa mendapatkan perilaku waras.
Stéphane Chazelas
1
Codepoint Unicode dalam UTF-8 dapat memiliki maksimum 4 oktet (atau byte), tetapi beberapa Karakter membutuhkan lebih dari satu codepoint, yang dapat menyebabkan urutan lebih lama dari 6 oktet.
12431234123412341234123
1
@ 12431234123412341234123, asli UTF-8 encoding mencakup hingga U + 7FFFFFFF (6 byte, dan ada beberapa ekstensi untuk naik ke 13 byte seperti perl's \x{7FFFFFFFFFFFFFFF}) dan sementara kisaran Unicode poin kode telah sewenang-wenang terbatas U + 10FFFF (karena keterbatasan desain UTF-16), beberapa alat masih mengenali / menghasilkan karakter 6 byte. Itulah yang saya maksudkan dengan 6 byte karakter. Dalam semantik Unix, satu karakter adalah satu codepoint. Anda lebih dari satu codepoint "karakter" yang lebih umum dirujuk sebagai cluster graphem disambiguate dari karakter.
Stéphane Chazelas
7

Cadalah lokal default, "POSIX" adalah alias dari "C". Saya kira "C" berasal dari ANSI-C. Mungkin ANSI-C mendefinisikan lokal "POSIX".

Edward Shen
sumber
Baik C dan UNIX jauh sebelum ANSI C.
a CVn
@ MichaelKjörling: Jadi? Saya telah melihat dokumentasi pra-ANSI, dan tidak memiliki lokal. Secara internal di AT&T Bell Labs, semua orang berbicara bahasa Inggris.
MSalters
@MSalters Fakta bahwa dokumentasi pra-ANSI untuk bahasa C tidak menyebutkan lokal (yang mungkin atau mungkin tidak menyiratkan bahwa pra-ANSI, C tidak memiliki konsep lokal; setelah semua, saya cukup yakin bahasanya masih tidak , tapi itu intinya) tidak menyiratkan bahwa Cnama lokal berasal dari "ANSI C".
CVn
2
@ MichaelKjörling: Anda tidak mengerti intinya. Ketika lokal diperkenalkan, "C" sudah berarti "ANSI C". Itu artinya K&R C di masa lalu tidak relevan.
MSalters
3

Sejauh yang saya tahu, OS X menggunakan urutan susunan titik kode di UTF-8 lokal, jadi ini merupakan pengecualian untuk beberapa poin yang disebutkan dalam jawaban oleh Stéphane Chazelas.

Ini mencetak 26 di OS X dan 310 di Ubuntu:

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

Kode di bawah ini tidak mencetak apa pun di OS X, menunjukkan bahwa input diurutkan. Enam karakter pengganti yang dihapus menyebabkan kesalahan urutan byte ilegal.

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

Kode di bawah ini tidak mencetak apa pun di OS X, yang menunjukkan bahwa tidak ada dua titik kode berurutan (setidaknya antara U + 000B dan U + D7FF) yang memiliki urutan susunan yang sama.

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

(Contoh-contoh di atas digunakan %bkarena printf \\U25menghasilkan kesalahan dalam zsh.)

Beberapa karakter dan urutan karakter yang memiliki susunan susunan yang sama di sistem GNU tidak memiliki susunan susunan yang sama di OS X. Ini mencetak ① pertama di OS X (menggunakan OS X sortatau GNU sort) tetapi ② pertama di Ubuntu:

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

Ini mencetak tiga baris di OS X (menggunakan OS X sortatau GNU sort) tetapi satu baris di Ubuntu:

export LC_ALL=en_US.UTF-8;printf %b\\n \\u0d4c \\u0d57 \\u0d46\\u0d57|sort -u
nisetama
sumber
Adakah yang tahu mengapa ada perbedaan ini?
1.61803
3

Tampaknya LC_COLLATEmengontrol "urutan abjad" yang digunakan oleh ls, juga. Lokal AS akan mengurutkan sebagai berikut:

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

pada dasarnya mengabaikan periode. Anda mungkin lebih suka:

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

Tentu saja saya lakukan. Pengaturan LC_COLLATEuntuk Cmencapai ini. Perhatikan bahwa ini juga akan mengurutkan huruf kecil setelah semua huruf besar:

A.C
A.H
AFilename.C
a.C
a.H
SteveInCO
sumber