単一文字リストのソート順の生成

単一文字リストのソート順の生成

場合によっては、個々の文字の組み合わせ順序を知る必要があります。通常、次のように表現されます。キャラクタークラス正規表現と同じです[b-d]。この文字クラスは次のように一致します。キャラクター与えられた範囲内で。

どの個人のb-d文字は範囲(または他の範囲)内の文字です。

また、C言語環境の組み合わせ順序は各ASCII文字のバイト値であることに注意してください。[ㅏ](33〜126の間の文字のみが表示されます):

!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

文字範囲をASCII以上に拡張できますか?

しかし:

ソート順はどうなりますか?個々の文字他の地域では?

(すべてのロケールで)これらのソート順を表示する方法はありますか?

[ㅏ]ASCIIを使用するシステム(ほとんどのシステム)では、他のシステムではEBCDICまたは他のシステムを使用することもできます。

答え1

これにはいくつかの側面があります。ロケールの文字セット内のすべての文字を一覧表示し、グラフィック文字(33〜126文字のASCII文字)を選択してソートする必要があります。

文字の並べ替え順序について話すのが妥当であるのか、それとも定義されたことがあるのか​​についての質問もあります。最後のポイントから始めます。

POSIX校正アルゴリズム

、または、シェルグローブまたは/、文字列比較演算子によって実装され使用されるC / POSIXソートアルゴリズム、およびより一般的にはPOSIXシステムでユーザーのロケールに基づいてテキストをソートするほとんどのツールについて話しているstrcoll()場合点に注意してください。sortlsawkexpr<>ひも

GNU システムの en_US.UTF-8 ロケールでは、é単一é文字で構成される文字列がソートされます。後ろに単一文字で構成される文字列eStéphaneソート済み今後 Stephanie。 cs_CZ.UTF-8ロケールでは、cとの間でソートしますが、間ではソートしません。bdchhi

対照アルゴリズムは、個々の文字ではなく文字列全体を考慮します。したがって、個別に比較するときに文字の順序を知ることは、その文字を含む文字列がどのように比較されるかを必ずしも知らせるわけではありません。

このアルゴリズムは、多くの実際の言語(辞書、電話帳など)などの文字列を比較するように設計されています。様々な文化の中で、ソートのすべての微妙さを扱うのはかなり単純です。集中治療室、より多くのアルゴリズムを実装しますが)ほとんどの場合は十分です。

このアルゴリズムでは要素の構成、これには文字だけでなく、韓国語のアルファベットやチェコ語のマルチパート文字などの文字の組み合わせも含まれます。chあるいは、一部のシステムでは、組み合わせの後に結合された予音(U + 0301)が複数の割り当てられた形式éで表示されます。e重量

そして完全な文字列比較は、最初の重みから最後の重みまで、順番に各重みを使用して実行されます。

たとえば、このen_US.UTF-8GNU ロケールでは、 、 、Eともに同じé基本重みを持ちます。そしてに分解eÉStéphaneStephanie

<S><t><é><p><h><a><n> <e>
<S><t><e><p><h><a><n> <i><e>

要素の組み合わせ(ここでは文字ごとに1つ)。

現時点では、n2つの文字列の対照要素は同じ主重みを持っていますが、の主i重みはesより大きいので、Stephanieソート後に二次Stéphane重みを考慮する必要はありません。

これでStephane、vsのStéphane場合は、基本重みを比較するときに等しくソートされるので、二次重みを考慮する必要があります。/usr/share/i18n/locales/iso14651_t1_commonen_US.UTF-8ロケールをそのまま使用してGNUシステムを調べると、次のことがわかります。

<BAS> # 15
[...]
<ACA> # 18
[...]
<U0065> <e>;<BAS>;<MIN>;IGNORE # 259 e
<U00E9> <e>;<ACA>;<MIN>;IGNORE # 260 é

ラテンアルファベット文字の場合、発音区別符号を比較するために補助重みが使用される。デフォルトの文字(BAS)は急性アクセント()のある文字の前にありますACA。したがって、Stéphane順序はにあります。と比較するには、Stephane英語で大文字が小文字の後にある3番目の重みに達する必要があります(たとえば、エストニア語とは対照的に)。STÉPHANEStéphane

IGNOREまた、最初の比較では、主な重みが考慮または考慮されていない空白や句読点などの英数字以外の文字もあります(de factoこれらの文字はおよび間に並べられますが、空白が間に並べられるという意味ではありません)。defacedegreefg

$'STE\u0301HANE'vsでは、Stéphane一部のシステム(Solarisなど)は、最後の文字が(U + 00C9)文字であることをE\u0301除いて、同じ重みを持つ対照要素として扱いますが、他のシステムはそれを句読点として処理して結果を提供します。 。良いです(以前のものと同じです)。É\u0301$'STE\u0301HANE'Stephane

一つでもないみんな注文する

GNUシステムではU + 0301のソート順序も定義されていません。この場合、何千もの文字があります。。私は丸められた数字(U + 2460 ... U + 2473)を例として使用したいと思います。なぜなら、この数字は明らかにソート順を持つべきですが、そうではないからです。

$ touch ① ② ③ ④ ⑤
$ ls
④  ③  ⑤  ②  ①
$ ls | sort -u

実際には、他の文字と同じ重みを持つように定義されたいくつかの文字もあります(たとえば、 ƎここƏƐは順序はすべて同じです)。

sortしたがって、一部の実装で実行する場合を除いて、一部のロケールでは、ランダムな文字をソートすることは実際には不可能です。これは、POSIX 仕様の次のメジャーバージョンの要件になります。)、memcmp()同じ順序の文字に対して同様の比較を実行できます。

ロケール文字セットのすべてのグラフィック文字のリスト

ロケールごとに異なる文字セットを使用できます。

文字セットには3つの主なカテゴリがあります。 ASCIIやiso-8859-xなどのシングルバイト文字セット(各バイトは文字に対応)(一部は定義されていない場合があります)、文字が異なるバイト数でエンコードされているUTF-8、GB18030、BIG5、またはEUCJPなどステートフルバイト

最後のカテゴリは最近ロケールでほとんど使用されず、管理が難しいことが多いので、今は無視しても構いません。

C ロケールは本質的に単一バイト文字セットを持つことが保証されます。 EBCDICベースではないシステムで一般的に見られますが、必ずしもASCIIである必要はありません。

一部のスクリプト(英語で使用されるラテンスクリプトなど)は左から右に、他のスクリプトは右から左に書かれているため、これらの他のスクリプト(一部の文字セットでサポートされている)の文字は同じ行にあります。必ずしも良い考えではありません。

キャラクターの組み合わせも同様ですが、結局ランダムなキャラクターに組み合わせて合わされることになります。

また、Unicodeなどの一部の文字セットは引き続き進化しています。現在、コードポイントの範囲は0..0xD7FF、0xE000..0x10FFFFに固定されていますが、ほとんどはまだ割り当てられておらず、新しいバージョンのUnicodeごとに新しいコードポイントの範囲が割り当てられ、システムベンダーはこれに合わせて努力しています。

graphとして分類された文字は、ISO / IEC TR 14652(2002)の後継であるISO / IEC 30112テクニカルレポート(2014)に記載されています。 GNUロケールはこれに従うようですが、他のロケール(FreeBSD / Solarisなど)はそうではありません。しかし、私にはあまり意味がないようだから、彼らを非難するわけではありません。たとえば、ほとんどのスペース文字は除外されますが、U+00A0(単純スペース)、U+2007(数値スペース)、またはU+200B(幅0のスペース)は除外されます。私の考えにはキャラクターが含まれていると思います。コントロールU+200C..U+200F, U+202D, U+202E...² のような文字カバーは右から左へこのQ&Aで重要なのは、文字の順序が左から右に変わることです。

$ printf '%b\n' '\u202E' a b c | sort | paste -sd '\0' -
‮abc

(一部のブラウザではサポートされているかどうかcba、他のブラウザではサポートされているかどうかが表示されますabc)。

また、ほとんどのコンテンツが含まれます。商標数値何千もの使用システムに描画できるのはもちろん、割り当てられる可能性もない文字です。

シングルバイト文字セット(GNUシステムではlocale ctype-mb-cur-max1を返す文字セット)の場合、グラフィック文字のリストはすべての255バイト値を繰り返す必要があります(最初を無視すると、各文字セットのNULはグラフィック文字セットではありません。)問題が発生する可能)と一致させます[[:graph:]]

awkたとえば、次のようにできます。

awk '
  BEGIN{
    for (i = 1; i < 256; i++) {
      c = sprintf("%c", i)
      if (c ~ /[[:graph:]]/) print c
    }
  }' | sort | paste -sd '\0' -

iso8859-7 シングルバイト文字セットを使用するギリシャ語ロケールでは、el_GR.iso88597以下が提供されます。

`^¨~<=>¦°_­-,;:!?/.·'ʽʼ"«»()[]{}§©@$£¤¥*\&#%+±ª―΄΅0½12²3³456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZΑαΆάΒβΓγΔδΕεΈέΖζΗηΉήΘθΙιΊίΪϊΐΚκΛλΜμΝνΞξΟοΌόΠπΡρΣσςΤτΥυΎύΫϋΰΦφΧχΨψΩωΏώ 

(後に続く切り捨て防止スペースは、GNUロケールの「グラフィック」として誤って分類されます。)

この方法はマルチバイト文字では使用できません。

または、文字セットエンコーディングをiconvサポートしている場合は、すべてのUnicodeコードポイントを32ビットのビッグエンディアン数で生成し、それをロケールの文字セットに変換できます。UCS-4BEUTF32BE

perl -e 'print pack("L>*", $_, 10) for 1..0xd7ff, 0xe000..0x10ffff' |
  iconv -cf UCS-4BE |
  grep '[[:graph:]]' |
  sort

またはUTF-8をサポートしている場合:

perl -C -M-warnings=nonchar -le 'print chr$_ for 1..0xd7ff, 0xe000..0x10ffff' |
  iconv -cf UTF-8 |
  grep '[[:graph:]]' |
  sort

(上記の問題を回避し、行が長すぎるのを防ぐために、1行に1文字ずつ予約してください。)

これは、Unicode(および対応するエンコード)が他のすべての可能な文字セットの文字を含むように設計されているため、すべての文字セット内のすべての文字には常にUnicodeコードポイントがあります。最新のシステムは、実際にはUnicodeの点で文字セットを定義し、通常はwchar_tUnicodeコードポイントに対応します。

上記のように、ソートはソートとmemcmp()同じ文字比較に基づく比較を使用しますstrcoll()。シングルバイト文字セットの場合、その文字セット内のコードポイントに基づいてソートされます。 UTF-8の場合、UTF-8には特定の属性があるため、Unicodeコードポイントに基づいてソートされます。他のUnicodeエンコーディング(中国語GB18030や他のマルチバイト文字セットなど)の場合、これは多少ランダムに表示されることがあります。

それにもかかわらず、これは、ソート順が同じ2つのロケールの場合、そのsortロケールが異なる文字セットを使用すると出力が異なることを意味します。

例えば、① ② ③ ④ ⑤ ⑥ ⑦ ─ ⑩⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳ 丸められた数字に戻ると。 Unicodeはこれを対応する順序(コードポイント0x2460〜0x2473)で指定します。 GNUロケールでは順序は定義されていません(①が②よりも前も後もありません)。 UTF-8を使用するロケールでは、UTF-8の順序はUnicodeコードポイントの順序に従うので、①の後に②をソートします。ただし、GB18030(中国の別のUnicodeエンコーディング)を使用するzh_CN.gb18030などのロケールでは、文字がバイトレベルでエンコードされる方法に応じて、⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳123456789⑩このエラーこれにより、手順は①②②③④⑤⑥⑧⑩⑮⑯⑰⑱⑲⑳⑪⑫⑬⑭)です。

照合順序に従って文字列の文字を並べ替えるには、次のようにしますzsh

printf "%s\n" ${(j::)${(s::o)string}}

zshは変数にNUL文字を含めることができますが、strcoll()これらの文字では機能しません。zshこの問題を解決しようとしましたが、完璧ではない

文字列に同じソート順を持つ別の文字が含まれている場合、結果は定義されません。


^2019年編集新しいバージョンのGNU libcでは、①②②③④⑤の順序が修正されましたが、2.30現在95%以上の文字がまだ定義されている順序はありません。

答え2

Bashとzshでは、一連の数字を生成するのは簡単です。

$ printf '\\x%x' {53..63}
\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f

UNICODE数値を文字に変換するのも簡単です。

$ printf '%b' "$(printf '\\U%x' {53..63})"
56789:;<=>?

これはbashコーダーのための贈り物であり、\UXXXXXXXX制限はありません。コード0から10FFFF(10進数で1114111)まで動作します。

したがって、次の簡単なコードを使用して最初の127文字を生成できます。

$ printf '%b' "$(printf '\\U%x' {0..127})" | hd
00000000  00 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f  |................|
00000010  10 11 12 13 14 15 16 17  18 19 1a 1b 1c 1d 1e 1f  |................|
00000020  20 21 22 23 24 25 26 27  28 29 2a 2b 2c 2d 2e 2f  | !"#$%&'()*+,-./|
00000030  30 31 32 33 34 35 36 37  38 39 3a 3b 3c 3d 3e 3f  |0123456789:;<=>?|
00000040  40 41 42 43 44 45 46 47  48 49 4a 4b 4c 4d 4e 4f  |@ABCDEFGHIJKLMNO|
00000050  50 51 52 53 54 55 56 57  58 59 5a 5b 5c 5d 5e 5f  |PQRSTUVWXYZ[\]^_|
00000060  60 61 62 63 64 65 66 67  68 69 6a 6b 6c 6d 6e 6f  |`abcdefghijklmno|
00000070  70 71 72 73 74 75 76 77  78 79 7a 7b 7c 7d 7e 7f  |pqrstuvwxyz{|}~.|
00000080

各文字にNUL(0x00)文字を追加し、sort -zを使用してソート順を表示できます。

$  printf '%b' "$(printf '\\U%x\\0' {0..127})" | sort -z
`^~<=>| _-,;:!?/.'"()[]{}@$*\&#%+


▒▒123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ

そして、NULを削除して数字の順序を確認してください。

$ printf '%b' "$(printf '\\U%x\\0' {0..127})" | sort -z | tr -d '\0' | hd
00000000  60 5e 7e 3c 3d 3e 7c 20  5f 2d 2c 3b 3a 21 3f 2f  |`^~<=>| _-,;:!?/|
00000010  2e 27 22 28 29 5b 5d 7b  7d 40 24 2a 5c 26 23 25  |.'"()[]{}@$*\&#%|
00000020  2b 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f  |+...............|
00000030  10 11 12 13 14 15 16 17  18 19 1a 1b 1c 1d 1e 1f  |................|
00000040  7f 30 31 32 33 34 35 36  37 38 39 61 41 62 42 63  |.0123456789aAbBc|
00000050  43 64 44 65 45 66 46 67  47 68 48 69 49 6a 4a 6b  |CdDeEfFgGhHiIjJk|
00000060  4b 6c 4c 6d 4d 6e 4e 6f  4f 70 50 71 51 72 52 73  |KlLmMnNoOpPqQrRs|
00000070  53 74 54 75 55 76 56 77  57 78 58 79 59 7a 5a     |StTuUvVwWxXyYzZ|
0000007f

Cロケールの順序との違いに注意してください。

$ printf '%b' "$(printf '\\U%x\\0' {0..127})" | LC_COLLATE=C sort -z | tr -d '\0' | hd
00000000  01 02 03 04 05 06 07 08  09 0a 0b 0c 0d 0e 0f 10  |................|
00000010  11 12 13 14 15 16 17 18  19 1a 1b 1c 1d 1e 1f 20  |............... |
00000020  21 22 23 24 25 26 27 28  29 2a 2b 2c 2d 2e 2f 30  |!"#$%&'()*+,-./0|
00000030  31 32 33 34 35 36 37 38  39 3a 3b 3c 3d 3e 3f 40  |123456789:;<=>?@|
00000040  41 42 43 44 45 46 47 48  49 4a 4b 4c 4d 4e 4f 50  |ABCDEFGHIJKLMNOP|
00000050  51 52 53 54 55 56 57 58  59 5a 5b 5c 5d 5e 5f 60  |QRSTUVWXYZ[\]^_`|
00000060  61 62 63 64 65 66 67 68  69 6a 6b 6c 6d 6e 6f 70  |abcdefghijklmnop|
00000070  71 72 73 74 75 76 77 78  79 7a 7b 7c 7d 7e 7f     |qrstuvwxyz{|}~.|
0000007f

バイトはASCIIの範囲外の128個の値を持つことができ、utf8として印刷されます。降伏(データソートでソートされていません):

$ printf '%b' "$(printf '\\U%x' {125..255})"
}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ
ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ

組み合わせ順序の同じ範囲は次のとおりです(最初の文字は急性アクセントであり、小文字と大文字のコレクションが交互に表示されます)。

$ printf '%b' "$(printf '\\U%x\\0' {125..255})" | sort -z
´¨~÷׬¦°µ¯­¡¿·¸«»}§¶©®¤¢£¥±¼½¾¹²³áÁàÀâÂåÅäÄãêæÆ
çÇðÐéÉèÈêÊëËíÍìÌîÎïÏñÑóÓòÒôÔöÖõÕøغßúÚùÙûÛüÜýÝÿþÞ

範囲が広がると、ソート順がさらに複雑になります。

$ printf '%b' "$(printf '\\U%x\\0' {0..500})" | sort -z | tr -d '\0'
´`^¨~÷×<=>¬|¦°µ _¯­-,;:!¡?¿/.·¸'"«»()[]{}§¶©®@¤¢$£¥*\&#%+
±ƀƂƃƄƅƉƋƌƍƑƒƔƖƗƚƛƜƝƞƤƥƦƧƨƩƪƫƬƭƮƱƲƸƹƺƻƼƽƾǀǁǂǃ

▒▒0¼½¾1¹2²3³456789aAáÁàÀăĂâÂǎǍåÅäǟÄǞãÃǡǠąĄāĀªæǣÆǢ
bBƁcCćĆĉĈčČċĊçÇƈƇdDďĎđĐƊðÐdzDzDZdžDžDŽeEéÉèÈĕĔêÊěĚëËėĖęĘēĒǝƎƏƐ
fFgGǴğĞĝĜǧǦġĠǥǤģĢƓƣƢhHĥĤħĦƕiIíÍìÌĭĬîÎǐǏïÏĩĨįĮīĪıİijIJ
jJĵĴǰkKǩǨķĶƙƘlLĺĹľĽŀĿłŁļĻljLjLJmMnNńŃňŇñÑņŅʼnŋŊnjNjNJ
oOóÓòÒŏŎôÔǒǑöÖƟőŐõÕøØǫǭǪǬōŌơƠºƆœŒpPqQĸrRŕŔřŘŗŖ
sSśŚŝŜšŠşŞſßtTťŤŧŦţŢuUúÚùÙŭŬûÛǔǓůŮüǘǜǚǖÜǗǛǙǕűŰũŨųŲūŪưƯ
vVwWŵŴƿxXyYýÝŷŶÿŸƴƳzZźŹžŽżŻƶƵþÞƷǯǮ

printfがutf8で文字をバイトペアに生成し、sort(純粋なCロケールを使用)がバイトソートを試みるため、これらの大きな文字グループをソートすることは純粋なCロケールでは不可能です。それ以外の場合は、\U…Cロケールでprintfが失敗します。必要なのは、1バイトに入ることができる256文字よりも多くの文字を許可する同等のロケールです。

$ LC_ALL=C.UTF-8 printf "$(printf '\\U%x' {32..500})" | sort
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ 
[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶· 
¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñò 
óôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭ 
ĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨ
ũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣ
ƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞ
ǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ

答え3

照合順序はロケール・ファイルによって定義されます。以下は、en_USロケールのすべてのASCII文字シーケンスです。

codes=$(for i in {32..126}; do echo "u$(printf '%04x' "$i")"; done)
grep_patterns=$(echo "$codes" | sed 's/^/^</; s/$/>/')
codes_locale=$(grep -oif - <<<"$grep_patterns" /usr/share/i18n/locales/iso14651_t1_common | tr -d '<>')

# "ASCII sequence"
for c in $codes; do echo -ne "\\$c"; done; echo
# "en_US sequence"
for c in $codes_locale; do echo -ne "\\$c"; done; echo

# OUTPUT:
#  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
#  !"#%&'()*+,-./:;<=>?@[\]^_`{|}~$0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

順序順序はソート順序と同じではないことを覚えておく必要があります。ソート中は追加のルールが適用されます。

関連情報