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

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

LinuxソートコマンドはCとC.utf-8ロケールを区別しますか?

ソートマニュアルでは、LC_ALL = Cを使用してバイト値でソートしますが、C.utf-8は(ASCIIだけでなく)utf8値も受け入れることがわかります。ただし、ソートマニュアルはこのロケールオプションをまったく参照しません。

LC_ALL=C sort file.txt と LC_ALL=C.utf8 sort file.txt を実行すると、2 つの間に違いは見られません。どちらもファイルにutf 8文字があるかどうかに関係なく動作するようです。

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

ベストアンサー1

LC_ALL=C sortバイト値でソートします。 ASCII1 だけでなく、すべての文字セットで作成された入力をバイト値でソートします。

UTF-8エンコーディングには、Unicodeコードポイントでソートするのと同じバイト値でソートする属性があります(memcmp()U + 1234より大きいエンコーディングまたは0x1234より小さいUnicodeコードポイントを見つけることができます)。

C.utf-8または(私の経験では、後者はより一般的です)POSIX標準化されていないロケールですが、見つかった場所ごとに文字セットがUTF-8であることを除いて、Cロケールのほとんどの属性を持つロケールを意味しますC.utf8C.UTF-8

LC_ALL=C.UTF-8 sort入力はコードポイントに基づいてソートされますが、比較する前にUTF-8をデコードするかstrcoll()重いstrxfrm()システムを呼び出すことになり、最終的にUTF-8の場合を使用するだけでmemcmp()十分です。

sortGNUとLinuxをカーネルとして使用する多くの非埋め込みオペレーティングシステムのlibc 使用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は2つの文字列が同じ場合は呼び出す前に呼び出し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-8GNUシステムでは:

$ 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言語環境では、LINE_MAXゼロ以外のバイト列で構成され、長さがバイトを超えない行はすべて有効なテキストです。 C.UTF-8には追加の制限があります。これはa\200bUTF-8 では無効であるためテキストではないため、sortPOSIX では対応するアクションは指定されません。

ちなみに、GNUシステムではメッセージング言語よりもLC_ALL=C優先されますが、優先順位はありません。$LANGUAGELC_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までの値のみが含まれていることに注意してください。CASCIIを使用するロケールは未定義の文字ですが、まだバイト128〜255を文字として扱います。ただし、ロケールC文字セットは文字ごとに1バイトを保証する必要があるため、Cロケール文字セットはUTF-8にすることはできません。

おすすめ記事