sort
次のような行で奇妙に振る舞うようです。>>b
$ cat test
a
>>b
b
c
>
>>
$ sort test
>
>>
a
b
>>b
c
私はその>>b
行が出力の3番目の行になると予想していましたが、sort
5番目の行です。なぜこれが起こり、sort
予想される結果を得る方法がありますか?
私はGNU / Linux Ubuntu 16.04を使用しています。
答え1
現代言語環境のソートアルゴリズムは非常に複雑です。
各キャラクター(実際には要素の構成複数の文字のシーケンスで構成できます (例: Czech ch
)。重みの設定ソート順序を決定します。
2つの文字列を比較するときは、すべての文字の最初の重みが最初に使用され、次に他の重みを使用して、2つの文字列が最初の重みと等しく揃っていることを確認します。
たとえば、多くのロケールでは、e
とé
はE
同じです。基本的な重み(同じ同等クラスに属し、両方が一致します[=e=]
)。
echo
したがって、たとえば、été
とをEnter
最初のパスで比較すると、デフォルトの重みは同じで、e
2番目の文字は順序(before before)を決定します。é
E
c
n
t
最初のパス以降été
、、、と比較するとÉté
同じランクが割り当てられるため、2番目のパスに2次重みを適用します。Ete
一般的なGNUロケールでは、ラテンスクリプト文字の2番目の重みがアクセントの優先順位を決定するために使用されます(アクセントのないもの、次のイエス、アクセントのあるもの、ブレベ、ミュートなど)。次に、été
合計を決定するために3番目の重みを使用する必要があります。Été
これは大文字と小文字に基づいています(ほとんどのロケールでは小文字から始まります)。体重も同じなのでランクも同じキャラクターもいます。
これは、人間と同様に、辞書と同じ方法でテキストをソートするために使用されます。
辞書では、スペースやほとんどの句読点も無視されることがわかります。たとえば、との間de facto
をdebut
並べ替えますdevoid
。空白文字の最初の重みはIGNOREです。
/usr/share/i18n/locales/iso14651_t1_common
GNUシステムでは、定義されたコア照合順序を見つけることができます(パスはディストリビューションによって異なります)。そこから以下を見ることができます:
ifdef UPPERCASE_FIRST
<CAP>
else
<MIN>
endif
[...]
ifdef UPPERCASE_FIRST
[...]
<MIN> # 10
[...]
else
[...]
<CAP> # 9
[...]
endif
[...]
order_start <SPECIAL>;forward;backward;forward;forward,position
<U0020> IGNORE;IGNORE;IGNORE;<U0020> # 32 <SP>
[...]
<U003E> IGNORE;IGNORE;IGNORE;<h> # 140 >
[...]
ifdef DIACRIT_FORWARD
order_start <LATIN>;forward;forward;forward;forward,position
else
order_start <LATIN>;forward;backward;forward;forward,position
endif
[...]
<U0065> <e>;<BAS>;<MIN>;IGNORE # 259 e
<U00E9> <e>;<ACA>;<MIN>;IGNORE # 260 é
[...]
<U0045> <e>;<BAS>;<CAP>;IGNORE # 577 E
<U00C9> <e>;<ACA>;<CAP>;IGNORE # 578 É
これは私たちが言ったことを示しています。スペースと>
最初の3つの重みはすべてに設定されますIGNORE
。最初の3つの重みが等しくソートされた文字列の場合にのみ、相対順序(>
スペースの前、<h>
未指定の組み合わせシンボルの前にリストされている<U0020>
)が考慮されます。
定義されたロケールUPPERCASE_FIRST
(たとえば/usr/share/i18n/locales/tr_TR
)では、大文字が最初に表示されます(3番目のパスで)。DIACRIT_FORWARD
一部のロケールと同様に、de_DE
2番目のパスでは発音区別記号の順序を逆に決定できます。
>
パス1、2、3でも同じ位置合わせを行います。 4番目の項目では、空の文字列がすべての項目よりも前に並べ替えられます。>>
>
>>
>>b
それ以降にソートしてください。b
最初の3つのパスでは同じようにソートされていますが、b
4番目のパスでは無視され、>
サイズが大きいためです。c
最初のパス(>
無視および前)b
より少ない。c
アイデアを得ることができます。
C
さて、ロケール定義を見てください。はるかに簡単です。重みは1つだけで、U+0000からU+10FFFFまでのコードポイント値に基づいています。したがって、SPC
(U + 0020)は>
(U + 003E)の前に並べられ、(U + 003E)はb
(U + 0062)の前に整列され、(U + 0063)はc
(U + 0063)の前に整列されます。どんなキャラクターも見落とされません。
少なくともGNU libcの場合、比較関数(strcoll()
したがって使用される共同)が含まれる場合、sort
Cロケール定義ファイルで定義された順序は無視されます。の値に関係なく、LC_CTYPE
およびはと同じLC_COLLATE=C
です。比較は常にバイト値に基づいているため、そのバイトがUnicodeコードポイントが反対方向に並べられた文字に対応していても(たとえば、ISO-8859-15の0xA4 U + 20AC EURO SIGN対A5 U + 00A5 YEN SIGN)文字セット)と(他に設定されていない場合)は同じ効果を持ちます。strcoll()
strcmp()
LC_ALL=C sort
LC_COLLATE=C sort
LC_ALL
答え2
sort(1)
マニュアルページから:
*** WARNING *** The locale specified by the environment affects sort order. Set
LC_ALL=C to get the traditional sort order that uses native byte values.
したがって、バイト値でソートするには、次のようにします。
LC_ALL=C sort test
それ以外のsort
場合、ソート可能なキーが見つかるまで先行文字は無視されます。これがまさに>>b
そのb
隣にある理由です。