sed が文字範囲と一致します。

sed が文字範囲と一致します。

特定のUnicode範囲を正確に一致させる方法はありますか?
使用しようキリル文字範囲例: U+400 ~ U+52f

以下を使用して、フル文字範囲(bashまたはzshから)を印刷できます。

$ echo -e $(printf '\\U%x' $(seq 0x400 0x52f)) ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁ҂҃҄҇ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡԢԣԤԥԦԧԨԩԪԫԬԭԮԯ

$ a=$(zsh -c 'echo -e $(printf '\''\\U%x'\'' $(seq 0x400 0x52f))')

特定の範囲でフィルタリングするには、0x452〜0x490を使用します。予想される出力は次のとおりです。

$ b=$(bash -c 'echo -e $(printf '\''\\U%x'\'' $(seq 0x452 0x490))')
$ echo "$b"
ђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁ҂҃҄҇ҊҋҌҍҎҏҐ
$ echo "$b" | xxd
00000000: d192 d193 d194 d195 d196 d197 d198 d199  ................
00000010: d19a d19b d19c d19d d19e d19f d1a0 d1a1  ................
00000020: d1a2 d1a3 d1a4 d1a5 d1a6 d1a7 d1a8 d1a9  ................
00000030: d1aa d1ab d1ac d1ad d1ae d1af d1b0 d1b1  ................
00000040: d1b2 d1b3 d1b4 d1b5 d1b6 d1b7 d1b8 d1b9  ................
00000050: d1ba d1bb d1bc d1bd d1be d1bf d280 d281  ................
00000060: d282 d283 d284 d285 d286 d287 d288 d289  ................
00000070: d28a d28b d28c d28d d28e d28f d290 0a    ...............

しかし、sedでフィルタリングすることは不可能に見えます。これはうまくいきません:

$ echo "$a" | sed 's/[^\x452-\x490]//g'

これも同じです(結果は他の文字と一致します(おそらく対照的な問題)。)

$ echo "$a" | sed $'s/[^\u452-\u490]//g' АБВГжзийклмнопрстуфхцчшщъыьэюяёђєѕіїјљњћќѝўџҋҍҏҐҗҙқҝҟҡңҥҧҩҫҭүұҳҵҷҹһҽҿӂӄӆӈӊӌӎӐӒӔӝӟӡӣӥӧөӫӭӯӱӳӵӹԅԇԉԋԍԏ

これもそうではありません(同じ照合順序の問題):

$ echo "$a" | sed 's/[^ђ-Ґ]//g'

これはawkで動作します。

$ echo "$a" | awk '{gsub(/[^ђ-Ґ]/,"")}1'

ただし、16進範囲を使用する唯一の方法は、シェルを使用して16進数をUnicode文字に変換することです。

$ echo "$a" | awk $'{gsub(/[^\u452-\u490]/,"")}1'

または(両方のソリューション):

$ c=$(bash -c 'printf "\u452-\u490"') 
$ echo "$a" | awk '{gsub(/[^'"$c"']/,"")}1'
$ echo $a | awk -v ra="[^$c]" '{gsub(ra,"")}1'

質問:

  • sedを使用してこれを行う方法はありますか?
  • より高いシェルなしで16進数でこれを行うことはできますか?

  • 可能であれば、sedが使用された照合順序と一致する範囲は正確に何ですかsed 's/[^ђ-Ґ]//g'

PS:Perlでできることを知っています。ありがとうございます。

ベストアンサー1

POSIXによると、角括弧式の範囲は、C / POSIXロケールに基づくコードポイントに対してのみ指定されています。他のロケールでは指定されておらず、通常は見つかった照合順序はある程度に基づいています。一部のロケールにはツールがあり、[g-j]時には一部のチェコロケールにも同じものがあることがわかります。iıǵIch

zsh[x-y]ロケールに関係なく、コードポイントに基づいて範囲が決定されるまれな範囲の1つです。シングルバイト文字セットの場合、これはバイト値に基づいており、マルチバイト文字セットの場合は、Unicodeコードポイントまたはシステムが表示するために使用するすべての項目に基づいています。ワイド文字内部的にはと共同です mbstowc()。 API(通常はUnicode)。

だからではzsh

  • [[ $char = [$'\u452'-$'\u490'] ]]
  • [[ $char = [^ђ-Ґ] ]]
  • y=${x//[^ђ-Ґ]/}

ロケールの文字セットがマルチバイトであり、これらの2文字を含む場合は、そのUnicode範囲の文字を一致させることができます。これらの文字の一部を含む単一バイト文字セット(たとえば、ほとんどの文字がU + 0401 ... U + 045FにあるISO8859-5)がありますが、これらの文字が使用されるロケールでは範囲がバイト値[ђ-Ґ](Unicodeコードポイントではなく、文字セットの対応するコードポイント)に基づいています。

Cロケールでは、範囲はコードポイントに基づいていますが、Cロケールの文字セットには次のものが含まれていることだけが保証されています。ポータブル文字セットこれはPOSIXまたはCコードを書くために必要ないくつかの文字です(これはキリル文字では見つかりません)。また保証されますシングルバイトしたがって、Unicodeで指定されたすべての文字を含めることはできません。実際に最も一般的に使用されるのはASCIIです。

実際には、C(または少なくとも1バイトの文字セットを持つロケール)に設定しない限り、LC_COLLATECに設定することはできません。LC_CTYPEただし、多くのシステムにはC.UTF-8ここで使用できるロケールがあります。

UTF-8は、すべてのUnicode文字とすべての文字セットのすべての文字を表すことができる文字セットの1つです。だからあなたはこれを行うことができます:

< file iconv -t utf-8 |
  LC_ALL=C.UTF-8 sh -c 'sed "$(printf "s/[^\321\222-\322\220]//g")"' |
  iconv -f utf-8

1つ目は、iconvユーザーのロケール文字セットをそれぞれU + 0452およびU + 0490のUTF-8\321\222および\322\220UTF-8エンコーディングに変換し、2つ目はiconvロケールの文字セットに戻します。

現在のロケールがすでにUTF-8を文字セットとして使用していて、fileその文字セットで作成されている場合は、次のように単純化できます。

<file LC_ALL=C.UTF-8 sed 's/[^ђ-Ґ]//g'

または:

<file LC_ALL=C.UTF-8 sed "$(printf "s/[^\321\222-\322\220]//g")"

GNUsedが提供する環境では、$POSIXLY_CORRECTエンコードされたバイト値に基づいて文字を指定できます。

<file LC_ALL=C.UTF-8 sed 's/[^\321\222-\322\220]//g'

以前のバージョンでは、次のものが必要な場合があります。

<file LC_ALL=C.UTF-8 sed 's/[^\o321\o222-\o322\o220]//g'

または16進変形:

<file LC_ALL=C.UTF-8 sed 's/[^\xd1\x92-\xd2\x90]//g'

マルチバイト文字セット(Unicodeのワイド文字表現ベースのシステムを含む)を使用するロケールの場合、別のオプションは次のようにawkGNUを使用することです。

awk 'BEGIN{for (i = 0x452; i<=0x490; i++) range = range sprintf("%c", i)}
     {gsub("[^" range "]", ""); print}'

(最初はPOSIXでGNU awkのように動作するにはawkの実装が必要だと思いましたが、POSIXでは次のように動作するにはawkの実装が必要なsprintf("%c", i)のでそうではありません。iコーディングロケールの文字(コードポイントではない)です。これは、マルチバイト文字で移植可能に使用できないことを意味します。

とにかくU + 0400 .. U + 052Fの範囲は、キリル文字の唯一のUnicode文字ではありません。スクリプト、キリル文字を文字として使用する言語は言うまでもありません。文字のリストはUnicodeのバージョンによって変わります。

Debian に似たシステムでは、以下を使用してリストを取得できます。

unicode --max 0 cyrillic

(Ubuntu 16.04では435個、Debian sidでは444個が与えられました(おそらく別のバージョンのUnicodeを使用したでしょう)。

で、、...perlを参照してUnicodeブロックを一致させ、そのバージョンが使用しているUnicodeバージョンに現在割り当てられているキリル文字の文字を一致させます(例を参照)。\p{Block: Cyrillic}\p{Block: Cyrillic_Ext_A,B,C}\p{Block: Cyrillic_Supplement}\p{Cyrillic}perlperl -MUnicode::UCD -le 'print Unicode::UCD::UnicodeVersion'

だから:

perl -Mopen=locale 's/\P{Cyrillic}//g'

おすすめ記事