次の内容を含むファイルから重複した行を削除したいと思います。シリア語スクリプト。ソースファイルには3行があり、最初と3行は同じです。
$ cat file.txt
ܐܒܘܢ
ܢܗܘܐ
ܐܒܘܢ
sort
andを使用すると、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
-c
UbuntuでのGNUの実装まったく同じ行ですが、同じ順序の連続行数です。
GNUシステムのほとんどの国際ロケールには、完全に関連していない多くの文字が同じソート順で定義されるバグがあります。ほとんどは、ソート順がまったく定義されていないためです。他のほとんどのオペレーティングシステムでは、すべての文字のソート順が異なります。
$ expr ܐ = ܒ
1
(expr
演算子=
、数値以外の引数の場合、オペランドの順序が等しい場合は1を返し、それ以外の場合は0を返します)。
ar_SY.UTF-8
これはまたはと同じですen_GB.UTF-8
。
必要なのは、これらの文字に異なる並べ替え順序が付与されたロケールです。 Ubuntuにシリア言語のロケールがある場合、これらの文字には別のソート順序が付与されると予想できますが、Ubuntuにはそのようなロケールはありません。
locale -a
サポートされているロケールリストの出力を表示できます。dpkg-reconfigure locales
として実行して、より多くのロケールを有効にできますroot
。localedef
の定義ファイルに基づいてより多くのロケールを手動で定義することもできますが、/usr/share/i18n/locales
シリア語のデータは見つかりません。
次の点に注意してください。
LC_COLLATE=syr_SY.utf8 cat file.txt | sort | uniq -c
コマンドにLC_COLLATE変数を設定するだけです。cat
これはファイルの内容の出力方法に影響を与えず、cat
テキストユーティリティではないため、コントラストや文字エンコーディングにも興味がありません。sort
との両方を設定しようとしていますuniq
。また、LC_CTYPE
UTF-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 ..
編集する
これは実際のソリューションよりもハッキングに近いです。ロケールテーブルによって提供される照合順序ではなく、個々のバイト値を使用して各行を処理する方法で機能しますsort
。uniq
使用する同等のロケール(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値がa
var値と同じ位置合わせにあることを意味します。[=…=]
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 ܢܗܘܐ