Unicode テキストで uniq を使用する

Unicode テキストで uniq を使用する

次の内容を含むファイルから重複した行を削除したいと思います。シリア語スクリプト。ソースファイルには3行があり、最初と3行は同じです。

$ cat file.txt 
ܐܒܘܢ
ܢܗܘܐ
ܐܒܘܢ

sortandを使用すると、uniq結果は3つの行がすべて同じであると仮定しますが、これは間違っています。

$ cat file.txt | sort | uniq -c
      3 ܐܒܘܢ

ロケールを明示的にシリア語に設定することも役に立ちません。

$ LC_COLLATE=syr_SY.utf8 cat file.txt | sort | uniq -c      
     3 ܐܒܘܢ

なぜこれが起こるのですか?それが重要であれば、私はKubuntu 18とbashを使用しています。

答え1

uniq-cUbuntuでのGNUの実装まったく同じ行ですが、同じ順序の連続行数です。

GNUシステムのほとんどの国際ロケールには、完全に関連していない多くの文字が同じソート順で定義されるバグがあります。ほとんどは、ソート順がまったく定義されていないためです。他のほとんどのオペレーティングシステムでは、すべての文字のソート順が異なります。

$ expr ܐ = ܒ
1

expr演算子=、数値以外の引数の場合、オペランドの順序が等しい場合は1を返し、それ以外の場合は0を返します)。

ar_SY.UTF-8これはまたはと同じですen_GB.UTF-8

必要なのは、これらの文字に異なる並べ替え順序が付与されたロケールです。 Ubuntuにシリア言語のロケールがある場合、これらの文字には別のソート順序が付与されると予想できますが、Ubuntuにはそのようなロケールはありません。

locale -aサポートされているロケールリストの出力を表示できます。dpkg-reconfigure localesとして実行して、より多くのロケールを有効にできますrootlocaledefの定義ファイルに基づいてより多くのロケールを手動で定義することもできますが、/usr/share/i18n/localesシリア語のデータは見つかりません。

次の点に注意してください。

LC_COLLATE=syr_SY.utf8 cat file.txt | sort | uniq -c

コマンドにLC_COLLATE変数を設定するだけです。catこれはファイルの内容の出力方法に影響を与えず、catテキストユーティリティではないため、コントラストや文字エンコーディングにも興味がありません。sortとの両方を設定しようとしていますuniq。また、LC_CTYPEUTF-8文字セットを使用してロケールを設定する必要があります。

システムにロケールがないため、これはsyr_SY.utf8ロケール(デフォルトロケール)を使用するのと同じです。C

実際、ここのCロケールまたはC.UTF-8はおそらく使用したいロケールです。

これらのロケールでは、組み合わせ順序はコードポイント、C.UTF-8のUnicodeコードポイント、Cのバイト値に基づいていますが、最終的にはその属性を使用するUTF-8文字エンコーディングと同じです。

$ LC_ALL=C expr ܐ = ܒ
0
$ LC_ALL=C.UTF-8 expr ܐ = ܒ
0

だから:

(export LANG=ar_SY.UTF-8 LC_COLLATE=C.UTF-8 LANGUAGE=syr:ar:en
 unset LC_ALL
 sort <file | uniq -c)

文字セットとしてUTF-8を含むLC_CTYPE、コードポイントベースのソート順序、およびシリア語またはアラビア語のエラーメッセージ(GNU coreutilsまたはメッセージがその言語に翻訳されているsort場合uniq)などのロケールに関連するその他の設定があります。まだ持っていません)。

これらを気にしない場合その他設定と使いやすさとポータブルに優れています。

<file LC_ALL=C sort | LC_ALL=C uniq -c

または

(export LC_ALL=C; <file sort | uniq -c)

@isaacがすでに示したように。


1 POSIX準拠のuniq実装は、ロケールソートアルゴリズムを使用した文字列比較ではなく、バイト間の同等比較を意味します。これは2018年版の標準でより明確になりました(参照:対応するオースティングループエラー)。しかし、GNUはuniq現在これを使用しており、strcoll()大文字と小文字を区別しない比較オプションがPOSIXLY_CORRECTあります。-iこれは皮肉なことに、ロケール情報を使用せずにASCII入力でのみ正しく機能します。

答え2

(簡単な)ポータブルソリューション:

$ ( LC_ALL=C sort syriac.txt | LC_ALL=C uniq -c )
      2 ܐܒܘܢ
      1 ܢܗܘܐ

シリア語のテキストをレンダリングできるフォントがない場合:

$ ( LC_ALL=C sort syriac.txt | LC_ALL=C uniq -c ) | xxd
00000000: 2020 2020 2020 3220 dc90 dc92 dc98 dca2        2 ........
00000010: 0a20 2020 2020 2031 20dc a2dc 97dc 98dc  .      1 .......
00000020: 900a                                     ..

編集する これは実際のソリューションよりもハッキングに近いです。ロケールテーブルによって提供される照合順序ではなく、個々のバイト値を使用して各行を処理する方法で機能しますsortuniq使用する同等のロケール(UTF-8「コードポイントソート順序」が「バイト値ソート順序」と同じ順序であるため)はですC.UTF-8

これはほとんどのAFAICTシステムで機能します。

同等の解決策は次のとおりです。

$ ( export LC_COLLATE=C.UTF-8; <syriac.txt sort | uniq -c )

基本的な問題はシリア語(Unicodeコードポイント)の文字ですU+0700–U+074F シリア語そしてU+0860-U+086F シリア語の補足資料)は照合順序の順序を設定していません。

/usr/share/i18n/localesこれは内部ロケール定義ファイル(debian / ubuntu)の問題ですless /usr/share/i18n/SUPPORTED

通常、ロケール名は通常「ll_CC」形式を取ります。ここで、「ll」はISO 639 2桁の言語コード、「CC」はISO 3166 2桁の国コードです。 そしてシリア語(西部の変形)Syrj

しかし、シリア語にはISO 639-2で3桁のコードが割り当てられています。そして639-2 コードの公式リスト

これ国コード(ISO 3166)は通常2文字コードです。おそらくSYでしょう。ISO 3166国コード一覧

ロケールに関連する1つまたはすべての環境変数を設定するだけでは不十分であり、すべてのテーブルが失われる(あなたの場合のように)失敗する可能性があります。このテーブルには、月名、平日、年式、時間形式、通貨形式、エラー報告言語(翻訳がある場合)などが設定されています。お読みください:ロケールを何に設定する必要がありますか?これはどのような影響を及ぼしますか?

Unicodeコードポイントに明示的に定義された照合順序がない場合、まったく同じように定義されない可能性があります。それがここで起こっていることです。

ファイルのコードポイントを一覧表示できます(1つのサンプルポイントのみを使用)。

$ echo $(cat syriac.txt | grep -oP '\X' | sort)
ܐ ܒ ܘ ܢ ܢ ܗ ܘ ܐ ܐ ܒ ܘ ܢ 

ただし、一意の値のみを取得しようとすると、すべての値が削除されます。

$ echo $(cat syriac.txt | grep -oP '\X' | sort -u )
ܐ

これは、すべての文字が同じ組み合わせ値(重み)を持つためです。

$ a=ܐ
$ b=ܒ
$ [[ $a == [=$b=] ]] && echo yes
yes

これは、var値がavar値と同じ位置合わせにあることを意味します。[=…=]b

代わりに繰り返されない文字がリストされます。

$ echo $(cat syriac.txt | grep -oP '\X' | LC_COLLATE=C.UTF-8 sort -u )
ܐ ܒ ܗ ܘ ܢ

答え3

最初のグループLC_CTYPE:

$ export LC_CTYPE=syr_SY.utf8
$ <infile sort |uniq -c
      2 ܐܒܘܢ
      1 ܢܗܘܐ

関連情報