LinuxのsortコマンドはCとC.utf-8ロケールを区別しますか?
ソートマニュアルでは、バイト値でソートするためにLC_ALL = Cを使用するように言われていますが、C.utf-8ではutf8値(ASCIIだけでなく)も許可されていることがわかりました。
LC_ALL = C sort file.txtとLC_ALL = C.utf8 sort file.txtを実行するとき、ファイルにutf 8文字が含まれているかどうかにかかわらず、どちらも機能しているようですが、2つの間に違いはありません。
既知の違いはありますか?
_LC_ALL=C sort
_は、バイト値でソートします。 ASCII¹だけでなく、任意の文字セットで記述された入力をバイト値でソートします。
UTF-8エンコーディングには、バイト値による並べ替えがUnicodeコードポイントによる並べ替えと同じであるという特性があります(memcmp()
は、U + 1234のエンコーディングがU + 1233または任意のUnicodeコードのエンコーディングよりも大きいことを検出します。 0x1234未満のポイント)。
_C.utf-8
_、_C.utf8
_または_C.UTF-8
_(後者の方が私の経験ではより一般的です)は、POSIXによって標準化されたロケールではありませんが、どこにあるかは、文字セットがUTF-8であることを除いて、Cロケールのほとんどのプロパティ。
_LC_ALL=C.UTF-8 sort
_は、コードポイントに基づいて入力をソートしますが、比較前にUTF-8をデコードするか、strcoll()
/strxfrm()
重い機械を呼び出す可能性があり、 UTF-8の場合、memcmp()
を使用すれば十分なので、無駄な労力がかかります。
Linuxをカーネルとして使用する多くの非組み込みOSで見られるGNU sort
およびGNU libc
を使用(ここでは、入力にNUL文字を追加しますGNU sort
strcoll()
はサポートしていませんが)をサポートしています:
_$ printf 'a\0£1\na\0€2\n' | LC_ALL=C ltrace -e strcoll -e memcmp sort
sort->memcmp("a\0\302\2431", "a\0\342\202\254", 5) = -1
a£1
a€2
$ printf 'a\0£1\na\0€2\n' | LC_ALL=C.UTF-8 ltrace -e strcoll -e memcmp sort
sort->strcoll("a", "a") = 0
sort->strcoll("\302\2431", "\342\202\2542") = -31
a£1
a€2
_
(実際には、比較する2つの文字列が同じバイト数の場合、GNU sort
が最初にmemcmp()
を呼び出してから、strcoll()
を呼び出すことがわかります。 memcmp()
はstrcoll()
に比べて非常に安価であるため、同じです。
その出力のいくつかのタイミングは1,000,000回繰り返されました:
_$ printf 'a\0£1\na\0€2\n%.0s' {1..1000000} > file.test
$ wc -mc file.test
10000000 13000000 file.test
$ time LC_ALL=C sort file.test > /dev/null
LC_ALL=C sort file.test > /dev/null 0.74s user 0.06s system 390% cpu 0.205 total
$ time LC_ALL=C.UTF-8 sort file.test > /dev/null
LC_ALL=C.UTF-8 sort file.test > /dev/null 6.04s user 0.12s system 522% cpu 1.179 total
_
したがって、UTF-8エンコードされたテキストをコードポイントでソートするには、C
または_C.UTF-8
_を使用しても機能的に違いはありませんが、C
の実装によってはsort
を使用する方が効率的です。
現在、バイトのすべてのシーケンスが有効なUTF-8を形成しているわけではないため、UTF-8以外の入力、つまりUTF-8としてデコードできないバイトのシーケンスを含む入力については、動作が異なる場合がありますC
と_C.UTF-8
_の間。まだGNUシステム上:
_$ print -l 'a\200b' 'a\201b' | LC_ALL=C sort -u
a�b
a�b
$ print -l 'a\200b' 'a\201b' | LC_ALL=C.UTF-8 sort -u
a�b
_
(ここで�は、端末エミュレーターによる不明なものの表現です)
C.UTF-8では、strcoll()
は、有効なUTF-8テキストを形成しない2つの文字列に対して0を返します。つまり、同じ並べ替え順序であると報告されます。
Cロケールでは、0以外で_LINE_MAX
_バイト以下のバイトのシーケンスで構成される行はすべて有効なテキストです。 C.UTF-8では、さらに制限があります。その_a\200b
_はUTF-8では無効であるため、テキストではないため、POSIXに従って、sort
の動作は規定されていません。
補足として:GNUシステムでは、メッセージの言語については_LC_ALL=C
_が_$LANGUAGE
_よりも優先されますが、_LC_ALL=C.UTF-8
_は優先されません。
_$ LC_ALL=C LANGUAGE=fr:es:en sort /
sort: read failed: /: Is a directory
$ LC_ALL=C.UTF-8 LANGUAGE=fr:es:en sort /
sort: échec de lecture: /: est un dossier
_
¹また、C
ロケール文字セットはASCIIに基づく必要はなく、ASCIIは0〜127の値のみをカバーすることに注意してください。ASCIIを使用するC
ロケール未定義の文字ではありますが、128〜255バイトを文字と見なします。ただし、C
ロケール文字セットは文字ごとに1バイトを保証する必要があるため、C
ロケール文字セットはUTF-8にできません