Gì C
giá trị so với LC_ALL
việ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 C
làm gì?
Gì C
giá trị so với LC_ALL
việ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 C
làm gì?
Câu trả lời:
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
LC_ALL
là 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ừ $LANGUAGE
trong 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 $LANG
theo 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-8
nếu bạn ở Thụy Sĩ nói tiếng Pháp, sử dụng UTF-8). Các LC_xxx
biến riêng lẻ ghi đè lên một khía cạnh nhất định. LC_ALL
ghi đè tất cả Các locale
lệ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 C
phươ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=C
và LC_ALL=POSIX
(hoặc LC_MESSAGES=C|POSIX
) ghi đè $LANGUAGE
, trong khi LC_ALL=anything-else
không.
Một vài trường hợp bạn thường cần đặt LC_ALL=C
:
sort -u
hoặ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 -u
khô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à C
miền địa phương đảm bảo).=
toán tử tuân thủ POSIX expr
hoặc ==
toán tử tuân thủ POSIX awk
( mawk
và gawk
khô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.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-Z
ký 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 a
và 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ư bash
cá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-8
trê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 y
với dấu phụ nhưng không phải là z
từ (kể từz
sắ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
. ksh93
tôn vinh các decimal_point
thiế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.
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 C
miề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 I
ngô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à İ
(lưu ý dấu chấm) ở đó và chữ thường I
là ı
(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à A
là 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_ALL
khô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.
C
ngữ 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 ở C
miề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 .
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ự.
C
là 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
tên miền địa phương xuất phát từ "ANSI C".
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 %b
vì printf \\U25
dẫ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 sort
hoặ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 sort
hoặ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
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
để C
thự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
xclock
cảnh báo (Missing charsets in String to FontSet conversion
), sẽ tốt hơn nếu bạn sử dụngLC_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~/.bashrc
tệp -export LC_ALL=C.UTF-8