正規化

Unicode 正規化形

[3] Unicode では、実際には同じ意味を表す列を複数の文字の列で表せることがあります。 この時、そのうちの一つの表現を代表として定めた上で、それへの変換方法を規定することができ、 その表現、あるいは変換方法を正規化形 (normalization form) といいます。 正規化形に変換することを正規化 (normalization) といいます。

仕様書

正規化形

[4] 正規化形にはいくつかのバリエーションがあります。

[5] 正規化形応用レベルの文脈上の制約を組み合わせた概念もあります。

[50] StringprepUnicode IDNA互換性処理PRECIS も広義の正規化と言えますが、 Unicode正規化に加えて大文字小文字の変換や禁止文字などその他の処理も組み合わさっています。 IDNA2008 による制約を満たす文字列も広義の正規化形と言えますが、やはり他の制約が組み合わさっています。

正規化形の性質

固有性

[24] 固有性 (uniqueness) Unicode 正規化形の最も重要な設計目標とされています。 2つの等価な文字列正規化形は完全に一致します >>6 7。すなわち、

安定性

[29] ここでいう安定性 (stability) 正規化に直接関係しない文字がそのまま無変更で残ることをいいます。具体的には、

効率性

[35] 正規化効率的 (efficiency) に実装可能であるとされています >>6 7。 具体的には、

  • [36] 正規化形が効率良く実装可能であって、とりわけ既に NFC または NFD である文字列に対して NFC を高速に得ることが可能です。 >>6 7
  • [37] 合成を行う正規化形は、必ずしも最初の形を生成するものではありません。 最小形の計算は高価である可能性があるためです。 >>6 7

安定性

[7] Unicode Consortium の規定する Unicode正規化形NFCNFDNFKCNFKD については、安定性、つまり Unicode の改訂を通じて正規化に非互換な変更が加わらないことがある程度保証されています。

[58] 基本的には、安定性文字が定義されている符号位置に関するものです。 新たな Unicodeの版で新しい文字が追加されると、その符号位置正規化の結果は変化するかもしれません。

強い正規化安定性

[10] 強い正規化安定性 (strong normalization stability) : Unicode 4.1 とそれ以降において、 ある版で割り当て済みの文字のみから構成される文字列正規化した結果は、 その文字列を以降の任意の版で正規化した結果と一致します。 >>9

[11] そのために、一旦割り当てられた文字分解写像正準結合クラスが改訂によって変化しないことが保証されています。 >>9

[12] この安定性は割り当て済みの文字のみで構成される場合についてのものであり、 未割当符号位置が含まれているときには必ずしも保証されません。

[38] この安定性が保証されるのは Unicode 4.1 以降ですが、実際には Unicode 3 以降においても Corrigendum #2, Corrigendum #3, Corrigendum #4, Corrigendum #5 の影響を受けない範囲では成立します。 >>6 11.2

弱い正規化安定性

[13] 弱い正規化安定性 (weak normalization stability) : Unicode 3.1 とそれ以降において、ある版で割り当て済みの文字のみから構成される文字列正規化した結果得られる文字列は、 以降の任意の版においても正規化済みです。また、それ以前で Unicode 3.1 までの版であって、 その文字列に含まれる文字すべてが含まれる版においてもまた、正規化済みです。 >>9>>6 11.1

[14] そのために、一旦割り当てられた文字分解写像がいくつかの例外ケースを除き改訂によって変化しないこと、 正準結合クラスが改訂によって変化しないことが保証されています。 >>9

[8] Unicode 正規化形合成版 (composition version) は「UCD 3.1.0」であると規定されています >>6 3。これは弱い正規化安定性が 3.1.0 以降について成立するということです。

[15] 強い正規化安定性が保証されれていれば、弱い正規化安定性もまた保証されます。

[18] 「XY」の2文字から「Z」の1文字への合成を追加する場合、 弱い正規化安定性が満たされるためには、 3文字すべてが新しい文字であるか、または XY のどちらかだけが古い文字であるかでなければなりません。 >>6 3

[20] また、正規化に影響するような既存の文字特性の変更も認められないことになります。 >>6 3

[16] 「Q + caron」という文字 (の列) を考えます。合成版である 3.1.0 にはこれを1符号位置で表す合成済文字は含まれていません。 基底文字結合文字の2文字で表すしかなく、これが正規形です。

[17] その後の版で、「Q + caron」を1文字で表す合成済文字が追加されたとしましょう。 自然に考えれば「Q-caron」 → 「Q」 + 「caron」という合成 (正準分解) を追加したいところですが、 そうすると以前の版で NFC だった「Q」 + 「caron」の2文字の表現が NFC ではなくなり、新しい「Q-caron」に正規化しなければならなくなってしまいます。 これは弱い正規化安定性の定義に反します。

[19] Unicode Consortium は新しい合成を追加することを非推奨としており、 従って「Q-caron」のような文字が後から追加されることは原則として無いようです。 >>6 3

Unicode 3.1 以前の非互換性

[22] Unicode 3.1.0 が「合成版」ということは、逆にそれまでの版と現在とでは互換性がない変更が行われていることを意味します。

[23] たとえば Unicode 2.1.9 においてハングル音節に関する互換性写像が削除され、 正規化の結果が変化しています。 >>6 6

[43] Unicode 3.0.1 と 3.1 の間に発行された訂正 #2 では、 誤って合成除外表から欠落していた1文字について、 合成除外表に追加され、非互換性が生じています。

Unicode 3.1 から Unicode 4.1 までの非互換性

[39] UAX #15 では、強い正規化安定性が保証されていない Unicode 4.1 よりも前の版との互換性が必要な時の対処方法として、

... の2通りを挙げて説明しています。

[42] >>41 は利用頻度が少ない文字だから問題ないことが多かろうなどと説明されていますが、 ひどいもんですなw

[44] Unicode 3.1 と 4.1 の間には訂正 #3訂正 #4が発行され、 6文字について正準分解が修正されています。

[45] Unicode 4.0.1 と 4.1 の間には訂正 #5 が発行され、「妨害」 の定義が修正されて正規化冪等性 (>>27) が保たれるようになっています。 それ以前も冪等性は設計目標に入っていましたが、定義の誤りにより厳密には成立していませんでした。

分解と合成

[55] Unicode では、符号化文字列文字列に対して正準分解正準合成互換分解といった演算が定義されています。 4つの正規化形は、その組み合わせとして次のように定義されています >>54

[67] 次のような手順群に整理できます >>54

  1. [64] 文字列完全分解を適用します。 NFC/NFD なら正準等価性を、NFKC/NFKD なら互換等価性を用います。
  2. [65] NFC/NFKC なら、
    1. [66] 文字列正準合成アルゴリズムを適用します。
  3. [68] それ以外なら、
    1. [63] 文字列正準再順序付けアルゴリズムを適用します。

大文字・小文字との関係

[52] >>51プログラミング言語識別子について大文字・小文字不区別正規化を両方採用する場合について考察しています。

正規化が好ましくない挙動を示すケース

[77] NFKC, NFKD は破壊的です。 データの意味を致命的に変化させてしまうことが多いです。 特別に望ましい理由があって避けられない場合を除き、 使うべきではありません。

[80] NFC が好ましいとされており、 NFD はそれと逆方向の変換であるため、 特に理由がなければ NFD は使うべきではありません。

[96] NFC はデータを破壊することがあります。 影響が完全に理解されている場合を除き使うべきではありません。


[79] CJK統合漢字との字形差をCJK互換漢字で表したものが、 正規化で破壊されるケースがあります。 (ありますというか、符号点の個数でいえばそのほうが多い。)

[87] その対策で導入された CJK互換漢字に対応した SVS は、 CJK互換漢字と同じものを表しますが、 正規化で同一視される関係にはありません。 大元の CJK互換漢字はただの CJK統合漢字と同一視されてしまうという、 ねじれた関係にあります。 SVS

つまり正規化はデータの破壊には役に立ちますが、 検索の役には立ちません。

[94] 日本常用漢字旧字体のかなり多くがCJK互換漢字で表されています。 正規化すると旧字体から新字体への文字化けが起こります。


[95] NFC, NFKC, NFKDハングルに適用すると望ましからざる結果になることがあります。 KS X 1026-1

[97] NFKC, NFKDハングル以外でも文字の意味を考慮しないで似た文字に変化させてしまうものなので、 ハングルで適用前後で文意が変わってしまうとしても特別に問題ということでもなさそうです。 (文意が変わってしまうケースはハングル関係なく NFKC, NFKD を使うべきではないので。)

[98] NFC古ハングルが混じったデータに適用するとハングル音節の構造を壊してしまうことがあります。 KS X 1026-1


[78] combining enclosing mark を使った合成済文字と結合文字列とで挙動が異なり、 正規化の対象から除外されているケースがあります。 combining enclosing mark

[86] 正準等価正規化により発生する結合文字の順序の入れ替えが好ましからざる変化をもたらすことがあります。 CGJ

[81] 単独のダイアクリティカルマークの多くは互換分解U+0020 + 結合文字ダイアクリティカルマークに分解されてしまいます。 U+0020 になることによって都合が悪いケースもあります。 結合文字

[82] ラテン文字キリル文字基底文字に重ねる形や一部を変形される形のダイアクリティカルマーク (overlaid diacritics) の扱いは一貫していません。 合成済文字と基底文字 + 結合文字の2種類の表現方法があっても、 大まかにいって、 数式越南用のものは正規化され、 それ以外のラテン文字系のものは正規化されないようです。 >>164, >>88

[89] 上下に付け加えるダイアクリティカルマークと違って字形の変化が単純ではないためのようです。 全然字形が違うのに結合文字合字で処理されるインド系文字とはずいぶん扱いが違います。

[84] 合字互換分解で分解されるとしても、 正準分解だけでは分解されません。 例えば U+FB01 は 「fi」 に互換分解される (正準分解されない) 合字です。

[83] つまり、 正規化に期待される効果、 例えば一致とか検索とか、 似た文字によるセキュリティー問題の回避のような類には、 Unicode で定められている正規化形では必ずしも十分ではありません。

[85] 合字結合文字が付いていた場合、 互換分解によって結合文字適用先が変化してしまいます。 >>164


[115] 正規化による結合文字の順序入れ替えや合成済文字結合列の置換は意味的に等価ということになっていますが、 操作前後のレンダリングは実装によるところが大きく、 必ずしも同じように表示されるとは限りません。 >>114

[116] 規格上はどうだ、意味的にはこう定義されている、といったところで現実はそうなっていません。

[114] PDF 千夜一夜: PDFと文字 (41) – Unicode標準形式NFCの問題点(続き), , https://blog.antenna.co.jp/PDFTool/archives/2006/02/pdf_41.html


[91] 何も考えずに NFCNFKC を使えばええやん、 と気づかずにデータを破壊する人々

[92] Normalization in HTML and CSS, , https://www.w3.org/International/questions/qa-html-css-normalization

[90] Unicode normalization could change the structure of a URL · Issue #626 · whatwg/url · GitHub, https://github.com/whatwg/url/issues/626

[93] 破壊的変換したら構造が破壊されるの、当たり前じゃんw

[101] >>99 字形の区別のために CJK互換漢字を使っている事例、 NFC だとハングル音節 + ハングル字母の2文字になるのをハングル字母3文字で表している事例。 これらで非 NFC が意図的に使われている。

[118] TwitterNFC 強制による被害事案: 異体字

応用

[61] Web では、一部で NFC が使われます。 XML完全正規化も使われます。 JavaScriptnormalize メソッドは、 NFCNFDNFKCNFKD に対応しています。

[62] それ以外の用法については、各正規化形の項を参照。

プロトコルの採用正規化法の衝突問題

[102] プロトコルAとプロトコルBを組み合わせるとき、プロトコルAとプロトコルBで採用する Unicode正規化形が違うと困ったことになります。

[103] 指定が NFCNFKC ならより厳しい NFKC にしておけばいいので問題ないです。 しかし指定が NFCNFD なら、同時に従うことができません。 たまたま正規化によって変化しない文字だけで構成されるなら両方同時に適用された状態にもなりますが、 そんなシステムは実用に耐えません。

[104] こうした不都合が起こらないよう、 プロトコル (文書形式データ構造等含め) は送信 (生成) において Unicode正規化を強制するべきではありません。

[105] これが現実にあまり問題視されないのは、

という理由と推測されます。

[108] あまり良く知らずにプロトコルの仕様書でおすすめされているからという理由で真面目に実装してしまうと、 罠にはまることになります。

実装

[57] NFCNFDNFKCNFKDFCCFCD を実装しています。 対応する Unicodeの版は、Perl の版に依存します。

[60] XS 版と PP 版があります。

関連

[21] >>12>>18 のようにありますが、合成除外表の規定によれば新たな正準分解が追加されるとすると、 その展開先の文字に既存の文字が含まれているなら合成除外表に追加しなければならないこととなっており、 場合によっては未定義の符号位置が含まれていても新しい版で結果が変わらないことが保証される場合もあるようです。

歴史

[109] Unicode Normalization thread should slow down; summary needed, L. David Baron, , , https://lists.w3.org/Archives/Public/www-style/2009Feb/0231.html

[110] i18n の専門家を称する人々は各種 Web標準Unicode正規化を要求するべきと主張していました (平成時代中期頃)。 しかし結局有効で有益で統一的な実装戦略を提出できませんでした。

[111] そのためいくつかの (当たり障りのない) 仕様に「できれば」程度の弱いおすすめとして潜り込ませることができたくらいで (またはまったく世の中に影響のない仕様の要件として提示できたくらいで)、 ほとんどの事業者はまったくスルーしました。

[112] そのおかげで有害無益な正規化で性能が激落ちするとか、 文字化けが多発するとか、 といった困った事案が Web標準が原因で発生することは避けられました。

[113] Web標準以外の要因では起きてしまっていますが、まあ不幸中の幸いといったところでしょうか。

メモ

[1] XProc: An XML Pipeline Language ( 版) http://www.w3.org/TR/2010/REC-xproc-20100511/#p.serialization

[2] XQuery 1.0 and XPath 2.0 Functions and Operators (Second Edition) ( ( 版)) http://www.w3.org/TR/2010/REC-xpath-functions-20101214/#func-normalize-unicode

[47] draft-duerst-i18n-norm - Character Normalization in IETF Protocols http://tools.ietf.org/html/draft-duerst-i18n-norm

[49] Character Model for the World Wide Web 1.0: Normalization ( ( 版)) http://www.w3.org/TR/2012/WD-charmod-norm-20120501/

[56] XPath and XQuery Functions and Operators 3.0 ( ( 版)) http://www.w3.org/TR/xpath-functions-3/#func-normalize-unicode

[69] XSLT 2.0 and XQuery 1.0 Serialization (Second Edition) ( ( 版)) http://www.w3.org/TR/2010/REC-xslt-xquery-serialization-20101214/#unicode-normalization

[70] XSLT 2.0 and XQuery 1.0 Serialization (Second Edition) ( ( 版)) http://www.w3.org/TR/2010/REC-xslt-xquery-serialization-20101214/#XML_NORMALIZATION-FORM

[71] XPath and XQuery Functions and Operators 3.1 () https://www.w3.org/TR/2017/REC-xpath-functions-31-20170321/#func-normalize-unicode

[72] XSLT and XQuery Serialization 3.1 () https://www.w3.org/TR/2017/REC-xslt-xquery-serialization-31-20170321/#XML_NORMALIZATION-FORM

[73] UTS #22: CharMapML () http://www.unicode.org/reports/tr22/tr22-8.html#att_normalization

[74] Regexp.ja · neologd/mecab-ipadic-neologd Wiki () https://github.com/neologd/mecab-ipadic-neologd/wiki/Regexp.ja

[76] UAX #44: Unicode Character Database (, ) https://www.unicode.org/reports/tr44/#NormalizationTest_txt

[117] ENSIP-15: Normalization Standard - ENS Documentation, https://docs.ens.domains/ens-improvement-proposals/ensip-15-normalization-standard