Những gì LC LCALL = Cv làm gì?


324

Cgiá trị so với LC_ALLviệc phải làm trong các hệ thống Unix-như thế nào?

Tôi biết rằng nó buộc cùng một địa phương cho tất cả các khía cạnh nhưng Clàm gì?


Nếu bạn muốn giải quyết vấn đề bằng xclockcảnh báo ( Missing charsets in String to FontSet conversion), sẽ tốt hơn nếu bạn sử dụng LC_ALL=C.UTF-8để tránh các vấn đề với cyrillic. Để đặt biến môi trường này, bạn phải thêm dòng sau vào cuối ~/.bashrctệp -export LC_ALL=C.UTF-8
fedotsoldier

@fedotsoldier có lẽ bạn nên đặt câu hỏi và tự đưa ra câu trả lời, tôi không nghĩ nó liên quan đến câu hỏi. Nó chỉ trả lời cho vấn đề khác nhau mà bạn đang gặp phải.
jcubic

Vâng, bạn đã đúng, ok
fedotsoldier

Câu trả lời:


209

Nó buộc các ứng dụng sử dụng ngôn ngữ mặc định cho đầu ra:

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

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

và các lực lượng sắp xếp theo byte-khôn ngoan:

$ 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 cho các ví dụ tốt, nhưng thiếu thông tin quan trọng trong câu trả lời của Stephane ...
Olivier Dulac

4
Bạn có ý nghĩa gì bởi ngôn ngữ mặc định ?
Stéphane Chazelas

2
Vâng, tôi hiểu tác giả có thể làm bất cứ điều gì anh ấy thích bao gồm cả không làm những gì nó nói trên tin. Điều này là. Tiếng Anh Mỹ là ngôn ngữ duy nhất có thể được trình bày chính xác với bộ ký tự trong LC_ALL = C, ngôn ngữ duy nhất có thứ tự sắp xếp theo LC_ALL = C (LC_COLLATE) có nghĩa, LC_ALL = C (LC_TIME) có tên tháng và ngày tiếng Anh. Tôi chưa bao giờ thấy các ứng dụng trong đó LC_ALL = C trả về tin nhắn bằng ngôn ngữ khác với LC_ALL = en LANGUAGE = en. Vì vậy, tôi có quyền báo cáo lỗi đối với chương trình nếu không phải như vậy? (không nói về các ứng dụng không được dịch sang tiếng Anh ở đây).
Stéphane Chazelas

2
Vấn đề là "Tiếng Anh Mỹ là ngôn ngữ duy nhất có thể được trình bày chính xác với bộ ký tự trong LC_ALL = C". Điều này thường chỉ đúng trong các chương trình C / C ++ khi sử dụng các ký tự hẹp, nhưng thậm chí sau đó vẫn có ngoại lệ (vì có một số ngôn ngữ chỉ sử dụng các ký tự và ký hiệu được tìm thấy trong ASCII). Báo cáo lỗi khi ngôn ngữ mặc định không phải là tiếng Anh sẽ khiến bạn có vẻ ... lớn.
Ignacio Vazquez-Abrams

3
Lưu ý rằng trong tiếng Anh (có nghĩa là LANG = en_US.utf8), các tin nhắn có thể (và nên) sử dụng các ký tự unicode như siêu ấn tượng để trích dẫn chuỗi. Trong khi đó trong LANG = C, nó chỉ có những cái ASCII (dấu ngoặc kép, backquote và dấu nháy đơn).
Ángel

332

LC_ALLlà biến môi trường ghi đè tất cả các cài đặt nội địa hóa khác ( ngoại trừ $LANGUAGEtrong một số trường hợp ).

Các khía cạnh khác nhau của các bản địa hóa (như ký tự nghìn dấu phân cách hoặc dấu thập phân, bộ ký tự, thứ tự sắp xếp, tháng, tên ngày, ngôn ngữ hoặc thông báo ứng dụng như thông báo lỗi, ký hiệu tiền tệ) có thể được đặt bằng một vài biến môi trường.

Thông thường bạn sẽ đặt $LANGtheo sở thích của mình với giá trị xác định khu vực của bạn (như fr_CH.UTF-8nếu bạn ở Thụy Sĩ nói tiếng Pháp, sử dụng UTF-8). Các LC_xxxbiến riêng lẻ ghi đè lên một khía cạnh nhất định. LC_ALLghi đè tất cả Các localelệnh, khi gọi mà không cần lập luận đưa ra một bản tóm tắt của các thiết lập hiện hành.

Chẳng hạn, trên hệ thống GNU, tôi nhận được:

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

Tôi có thể ghi đè một cài đặt riêng lẻ chẳng hạn:

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

Hoặc là:

$ LC_MONETARY=fr_FR.UTF-8 locale currency_symbol
€

Hoặc ghi đè mọi thứ với LC_ALL.

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

Trong tập lệnh, nếu bạn muốn buộc một cài đặt cụ thể, vì bạn không biết người dùng đã buộc cài đặt nào (có thể là LC_ALL), tùy chọn tốt nhất, an toàn nhất và duy nhất của bạn là bắt buộc LC_ALL.

Địa Cphương là một địa phương đặc biệt có nghĩa là địa phương đơn giản nhất. Bạn cũng có thể nói rằng trong khi các địa phương khác dành cho con người, thì ngôn ngữ C dành cho máy tính. Trong ngôn ngữ C, các ký tự là các byte đơn, bộ ký tự là ASCII (tốt, không bắt buộc, nhưng trong thực tế sẽ có trong các hệ thống mà hầu hết chúng ta sẽ sử dụng), thứ tự sắp xếp dựa trên các giá trị byte, ngôn ngữ thường là tiếng Anh Mỹ (mặc dù đối với tin nhắn ứng dụng (trái ngược với những thứ như tên tháng hoặc ngày hoặc thư của hệ thống thư viện), theo ý của tác giả ứng dụng) và những thứ như ký hiệu tiền tệ không được xác định.

Trên một số hệ thống, có một sự khác biệt với miền địa phương POSIX, ví dụ, thứ tự sắp xếp cho các ký tự không phải ASCII không được xác định.

Bạn thường chạy một lệnh với LC_ALL = C để tránh các cài đặt của người dùng can thiệp vào tập lệnh của bạn. Ví dụ, nếu bạn muốn [a-z]để phù hợp với nhân vật 26 ASCII từ ađến z, bạn phải thiết lập LC_ALL=C.

Trên các hệ thống GNU LC_ALL=CLC_ALL=POSIX(hoặc LC_MESSAGES=C|POSIX) ghi đè $LANGUAGE, trong khi LC_ALL=anything-elsekhông.

Một vài trường hợp bạn thường cần đặt LC_ALL=C:

  • sort -uhoặc sort ... | uniq.... Ở nhiều địa phương khác ngoài C, trên một số hệ thống (đáng chú ý là GNU), một số ký tự có cùng thứ tự sắp xếp . sort -ukhông báo cáo các dòng duy nhất, nhưng một trong mỗi nhóm dòng có thứ tự sắp xếp bằng nhau. Vì vậy, nếu bạn muốn các dòng duy nhất, bạn cần một miền địa phương trong đó các ký tự là byte và tất cả các ký tự có thứ tự sắp xếp khác nhau (mà Cmiền địa phương đảm bảo).
  • điều tương tự cũng áp dụng cho =toán tử tuân thủ POSIX exprhoặc ==toán tử tuân thủ POSIX awk( mawkgawkkhông phải là POSIX về vấn đề đó), không kiểm tra xem hai chuỗi có giống nhau hay không nhưng chúng có sắp xếp giống nhau không.
  • Phạm vi nhân vật như trong grep. Nếu bạn muốn ghép một chữ cái trong ngôn ngữ của người dùng, hãy sử dụng grep '[[:alpha:]]'và không sửa đổi LC_ALL. Nhưng nếu bạn muốn khớp các a-zA-Zký tự ASCII, bạn cần LC_ALL=C grep '[[:alpha:]]'hoặc LC_ALL=C grep '[a-zA-Z]'. [a-z]khớp với các ký tự sắp xếp sau avà trước z(mặc dù với nhiều API, nó phức tạp hơn thế). Ở các địa phương khác, bạn thường không biết đó là những gì. Ví dụ, một số trường hợp bỏ qua địa phương để sắp xếp để [a-z]trong một số API như bashcác mẫu, có thể bao gồm [B-Z]hoặc [A-Y]. Trong nhiều địa phương UTF-8 (bao gồm en_US.UTF-8trên hầu hết các hệ thống), [a-z]sẽ bao gồm các chữ cái Latinh từ ađến yvới dấu phụ nhưng không phải là ztừ (kể từzsắp xếp trước họ) mà tôi không thể tưởng tượng sẽ là những gì bạn muốn (tại sao bạn muốn bao gồm évà không ź?).
  • số học dấu phẩy động trong ksh93. ksh93tôn vinh các decimal_pointthiết lập trong LC_NUMERIC. Nếu bạn viết một tập lệnh có chứa a=$((1.2/7)), nó sẽ ngừng hoạt động khi được chạy bởi người dùng có ngôn ngữ có dấu phẩy là dấu phân cách thập phân:

    $ 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
    

    Sau đó, bạn cần những thứ như:

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

    Như một lưu ý phụ: ,dấu tách thập phân mâu thuẫn với ,toán tử số học có thể gây ra nhiều nhầm lẫn hơn nữa.

  • Khi bạn cần các ký tự là byte. Ngày nay, hầu hết các địa phương đều dựa trên UTF-8, có nghĩa là các ký tự có thể chiếm từ 1 đến 6 byte. Khi xử lý dữ liệu có nghĩa là byte, với các tiện ích văn bản, bạn sẽ muốn đặt LC_ALL = C. Nó cũng sẽ cải thiện hiệu suất đáng kể vì phân tích dữ liệu UTF-8 có chi phí.
  • một hệ quả của điểm trước: khi xử lý văn bản mà bạn không biết ký tự nào được đặt trong đầu vào, nhưng có thể cho rằng nó tương thích với ASCII (vì hầu như tất cả các bộ ký tự là). Chẳng hạn, grep '<.*>'để tìm các dòng chứa a <, >cặp sẽ không hoạt động nếu bạn đang ở trong miền địa phương UTF-8 và đầu vào được mã hóa trong một bộ ký tự 8 bit một byte như iso8859-15. Đó là bởi vì .chỉ khớp các ký tự và các ký tự không phải ASCII trong iso8859-15 có khả năng không tạo thành một ký tự hợp lệ trong UTF-8. Mặt khác, LC_ALL=C grep '<.*>'sẽ hoạt động vì bất kỳ giá trị byte nào tạo thành một ký tự hợp lệ trong Cmiền địa phương.
  • Bất cứ lúc nào bạn xử lý dữ liệu đầu vào hoặc dữ liệu đầu ra không dành cho / cho con người. Nếu bạn đang nói chuyện với người dùng, bạn có thể muốn sử dụng quy ước và ngôn ngữ của họ, nhưng chẳng hạn, nếu bạn tạo một số số để cung cấp cho một số ứng dụng khác mong đợi dấu thập phân kiểu tiếng Anh hoặc tên tháng tiếng Anh, bạn sẽ muốn đặt 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
    

    Điều đó cũng áp dụng cho những thứ như so sánh trường hợp không nhạy cảm (như trong grep -i) và chuyển đổi trường hợp ( awk's toupper(), dd conv=ucase...). Ví dụ:

    grep -i i
    

    không được đảm bảo để phù hợp với Ingôn ngữ của người dùng. Ví dụ, ở một số địa phương Thổ Nhĩ Kỳ, nó không phải là chữ hoa iİ(lưu ý dấu chấm) ở đó và chữ thường Iı(lưu ý dấu chấm bị thiếu).


Tùy thuộc vào mã hóa của văn bản, điều đó không nhất thiết phải làm đúng. Điều đó hợp lệ đối với các bộ ký tự UTF-8 hoặc byte đơn (như iso-8859-1), nhưng không nhất thiết phải là bộ ký tự đa nhân không UTF-8.

Ví dụ: nếu bạn đang ở một zh_HK.big5hkscsđịa phương (Hồng Kông, sử dụng biến thể Hồng Kông của mã hóa ký tự Trung Quốc BIG5) và bạn muốn tìm các chữ cái tiếng Anh trong một tệp được mã hóa trong bảng mã đó, hãy thực hiện:

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

hoặc là

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

sẽ là sai, bởi vì trong bộ ký tự đó (và nhiều bộ khác, nhưng hầu như không được sử dụng kể từ khi UTF-8 xuất hiện), rất nhiều ký tự chứa các byte tương ứng với mã hóa ASCII của các ký tự A-Za-z. Chẳng hạn, tất cả A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽(và nhiều thứ khác) chứa mã hóa của A. là 0x96 0x41 và Alà 0x41 như trong ASCII. Vì vậy, chúng ta LC_ALL=C grep '[a-zA-Z]'sẽ khớp với các dòng có chứa các ký tự đó vì nó sẽ hiểu sai các chuỗi byte đó.

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

sẽ hoạt động, nhưng chỉ khi LC_ALLkhông được đặt khác (sẽ ghi đè LC_COLLATE). Vì vậy, bạn có thể sẽ phải làm:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

nếu bạn muốn tìm các chữ cái tiếng Anh trong một tệp được mã hóa theo mã hóa của miền địa phương.


12
+1, đó là câu trả lời tốt nhất (để chỉ ra phần ghi đè, v.v.). Nhưng thiếu các ví dụ (hay) về câu trả lời của Ignacio ^^
Olivier Dulac

1
Một nitlog nhỏ: Ngôn Cngữ chỉ được yêu cầu để hỗ trợ "bộ ký tự di động" (ASCII 0-127) và hành vi cho ký tự> 127 là không xác định về mặt kỹ thuật . Trong thực tế, hầu hết các chương trình sẽ coi chúng là dữ liệu mờ và chuyển chúng qua như bạn mô tả. Nhưng không phải tất cả: cụ thể, Ruby có thể bị nghẹt dữ liệu char với byte> 127 nếu chạy ở Cmiền địa phương. Tôi thực sự không biết nếu đó là "phù hợp" về mặt kỹ thuật, nhưng chúng ta đã thấy nó trong tự nhiên .
Andrew Janke

2
@AndrewJanke, vâng. Lưu ý rằng bộ ký tự di động không có nghĩa là ASCII cũng như 0-127. Đã có rất nhiều cuộc thảo luận về danh sách gửi thư của nhóm Austin về các thuộc tính của bộ ký tự địa phương "C" sẽ là gì và sự đồng thuận chung (và điều đó sẽ được làm rõ trong thông số tiếp theo) là bộ ký tự sẽ là đơn byte và bao gồm toàn bộ phạm vi 8 bit (với các thuộc tính được mô tả ở đây). Trong thời gian trung bình, có, có thể có một số phân kỳ (như lỗi hoặc do thông số kỹ thuật không đủ rõ ràng). Trong mọi trường hợp, LC_ALL = C là gần nhất bạn có thể có hành vi lành mạnh.
Stéphane Chazelas

1
Một điểm mã Unicode trong UTF-8 có thể có tối đa 4 octet (hoặc byte), nhưng một số Ký tự cần nhiều hơn một điểm mã, có thể dẫn đến chuỗi dài hơn 6 octet.
12431234123412341234123

1
@ 12431234123412341234123, mã hóa UTF-8 ban đầu bao gồm tối đa U + 7FFFFFFF (6 byte và có một số tiện ích mở rộng lên đến 13 byte như perl's \x{7FFFFFFFFFFFFFFF}) và trong khi phạm vi các điểm mã Unicode đã bị giới hạn tùy ý ở U + 10FFFF (do giới hạn thiết kế UTF-16), một số công cụ vẫn nhận dạng / tạo ra các ký tự 6 byte. Đó là những gì tôi có nghĩa là 6 ký tự byte. Trong ngữ nghĩa Unix, một ký tự là một điểm mã. Nhiều "ký tự" mã hóa của bạn thường được gọi chung là các cụm biểu đồ để phân tán khỏi các ký tự.
Stéphane Chazelas

7

Clà miền địa phương mặc định, "POSIX" là bí danh của "C". Tôi đoán "C" có nguồn gốc từ ANSI-C. Có thể ANSI-C định nghĩa miền địa phương "POSIX".


Cả C và UNIX đều có trước ANSI C.
một CVn

@ MichaelKjorling: Vậy sao? Tôi đã xem tài liệu trước ANSI và nó không có ngôn ngữ. Nội bộ tại AT & T Bell Labs, mọi người đều nói tiếng Anh.
MSalters

@MSalters Thực tế là tài liệu tiền ANSI cho ngôn ngữ C không đề cập đến các ngôn ngữ (có thể có hoặc không có nghĩa là trước ANSI, C không có khái niệm về các ngôn ngữ; sau tất cả, tôi khá chắc chắn rằng ngôn ngữ vẫn không , nhưng đó là bên cạnh điểm) không ngụ ý rằng Ctên miền địa phương xuất phát từ "ANSI C".
một CVn

2
@ MichaelKjorling: Bạn đang thiếu điểm. Khi các ngôn ngữ được giới thiệu, "C" có nghĩa là "ANSI C". Điều đó có nghĩa là K & R C trong quá khứ là không liên quan.
MSalters

3

Theo như tôi có thể nói, OS X sử dụng thứ tự đối chiếu điểm mã trong các ngôn ngữ UTF-8, do đó, đây là một ngoại lệ đối với một số điểm được đề cập trong câu trả lời của Stéphane Chazelas.

Điều này in 26 trong OS X và 310 trong Ubuntu:

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

Mã bên dưới không in gì trong OS X, chỉ ra rằng đầu vào được sắp xếp. Sáu ký tự thay thế được loại bỏ gây ra lỗi chuỗi byte bất hợp pháp.

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

Mã bên dưới không in gì trong OS X, chỉ ra rằng không có hai điểm mã liên tiếp (ít nhất là giữa U + 000B và U + D7FF) có cùng thứ tự đối chiếu.

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

(Các ví dụ trên sử dụng %bprintf \\U25dẫn đến lỗi trong zsh.)

Một số ký tự và chuỗi ký tự có cùng thứ tự đối chiếu trong các hệ thống GNU không có cùng thứ tự đối chiếu trong OS X. Điều này in ① đầu tiên trong OS X (sử dụng OS X sorthoặc GNU sort) nhưng đầu tiên là trong Ubuntu:

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

Điều này in ba dòng trong OS X (sử dụng OS X's sorthoặc GNU sort) nhưng một dòng trong Ubuntu:

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

Có ai biết tại sao có sự khác biệt này?
1.61804

3

Dường như LC_COLLATEđiều khiển "thứ tự chữ cái" được sử dụng bởi ls. Địa phương Hoa Kỳ sẽ sắp xếp như sau:

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

về cơ bản bỏ qua các thời kỳ. Bạn có thể thích:

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

Tôi chắc chắn. Thiết lập LC_COLLATEđể Cthực hiện điều này. Lưu ý rằng nó cũng sẽ sắp xếp chữ thường sau tất cả các chữ viết hoa:

A.C
A.H
AFilename.C
a.C
a.H
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.