.Net、UTF-16および正規表現

どういうわけか、XML名が正しいかどうかを確認する必要がありました。 何がもっと簡単だろうか? 標準を参照します。名前を開始できる記号と継続できる記号が明確に記述されており、すべてがシンプルで明確です。



[4] NameStartChar ::= ":" | [AZ] | "_" | [az] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]

[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]

[5] Name ::= NameStartChar (NameChar)*









ほぼ既製の正規表現、簡単なファイル処理Ctrl + H ...



public const string NameStartCharPattern = @"\:|[AZ]|_|[az]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]|[\u10000-\uEFFFF]" ;

public const string NameCharPattern = NameStartCharPattern + @"|-|\.|[0-9]|\u00B7|[\u0300-\u036F]|[\u203F-\u2040]" ;

public const string NamePattern = @"(?:" + NameStartCharPattern + @")(?:" + NameCharPattern + @")*" ;


* This source code was highlighted with Source Code Highlighter .








私たちはテストを書いています...

Assert.That(Regex.Match( "4a" , Patterns.NamePattern), Is.False);

* This source code was highlighted with Source Code Highlighter .






クリーン、シンプル、クリア...落ちた!



悪の根は、最初の行の最後のコンポーネントであることが判明しました:[\ u10000- \ uEFFFF]。 彼はすべきではありませんが、すべてのキャラクターを捕まえます...やめなさい、彼はどのように捕まえますか? UTF-16があり、文字は2バイトに制限されていますか?..または制限されていませんか?..



エンコーディングの分野での私自身の非識字の撲滅と、ここで短い形式で示した私の教育の結果に緊急に対処しなければなりませんでした。 誰かがこれらの事実を長い間馴染みのあるものと思っているなら、次のパラグラフをスキップしてください。



Unicodeには65536文字をはるかに超える文字をエンコードする機能があることがわかりました。 Unicode文字は、いわゆるプレーンに分割され、各プレーンは0x10000文字の容量を持ちます。 プログラマーの観点から見たこのような「曲線」は、ここでは理由がないわけではありません。実際、1つのプレーンが処理され、16が他の方法で処理されます。 最初の、いわゆる基本的な多言語面は、頭字語BMPでも知られ、今日使用されているすべての記号の大部分を含んでいます。 UTF-16でのエンコード時のすべての文字は、2バイトで、つまり文字コードに直接対応する2バイトで書き込まれます。 コードの特別な範囲である0xD800-0xDFFFは、同じプレーンで定義されます。 サロゲートと呼ばれる2048個の値が含まれています。 それ自体では、これらの値はペアでのみUTF-16で見つけることはできません-2ワード(2 x 2バイト)は次の16のパネルから値を次のように設定します:0x10000は文字コードから減算され、きれいな20ビット数を与えます。 これらの20ビットは、1番目と2番目のワードに10が書き込まれるため、2048個の選択されたコードを占有します。 さらに、最初の単語は接頭辞0b110110で書き込まれ(値0xD800-0xDBFFを与え、上位または先頭サロゲートと呼ばれます)、2番目は0b110111(それぞれ0xDC00-0xDFFF、最終または下位サロゲート)であるため、外部の各単語の目的が明確に決定されますコンテキストに応じて。



...それで、.Netのように見えるでしょうか? また、代理変数を操作するためのツールを提供しますが、正規表現エンジンは代理変数を無視します。 つまり、一般的に無視し、文字のペアとしてそれらを使用します。 そのような場合にいつものように、私はこの問題を最初に発見したわけではありません。 繰り返しますが、いつものように、Microsoftの評決は「修正しない」です。



だから、どういうわけか一緒に暮らさなければならない。 バグレポートで示唆されているように、スズメに沿って大砲から-サードパーティのエンジンをPInvoke経由で呼び出します。 2番目のアイデア-これらのサロゲートの一般的なサポートで地獄に行くのは魅惑的でしたが、私はあきらめないことに決めました...そして、突然バグが機能として使用できることに気付きました!



私たちの場合、サロゲートで動作するグループの構造は非常に単純です-実際、最初の14プレーンの文字を許可し、最後の2つを禁止します...つまり、サロゲートの高い領域から特定の範囲の値を禁止し、式を次のように置き換えることができます:

[\u10000-\uEFFFF] -> (?:[\uD800-\uDB7F][\uDC00-\uDFFF])







この方法はあまり一般的ではなく、狭い範囲の文字を設定するのはひどく不便で、私には美しく見えたので、それをあなたと共有することにしました。



All Articles