web-dev-qa-db-ja.com

LC_ALL = CとLC_ALL = C.utf8を並べ替え

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つの間に違いはありません。

既知の違いはありますか?

7
Binyamin

_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 sortstrcoll()はサポートしていませんが)をサポートしています:

_$ 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にできません

8