2017.07.27

IPv6のアクセスが20%に達する

前回の記事では、IPv6でのGoogleへのアクセスが全体に対し2015/12/31に10%に達したと述べたが、同じサイトで 2017/7/22 に 20.10% に達したことがわかった。

5%から10%は15ヶ月かかっていたが、10%から20%は19ヶ月かかったことになる。2016年12月のInternet Watchでの予想では、「2019年に50%を超える」とのことである (このペースではちょっと厳しいか?)。

以前はIPv6には「キラーアプリケーションがない」と言われることもあったが、IPv6とIPv4の両方で同じサービスが受けられることを前提にしている今のやり方では、IPv6のみでアクセスできるアプリケーションなどあるはずがない (あってはいけない)。私は、IPv6の普及には、IPv6のほうが安い・高速・安定しているといった接続環境としての差別化が必要になると考えている。

土日に比率が高くなる傾向は以前から続いているが、最近は金曜日から比率が高まる状況にある。これはインドでの普及率の上昇 (と、その背景にあるIPv4アドレスの枯渇) が原因にあるのかもしれない。モバイル環境からのIPv4のアクセスがNAT経由になると、IPv6のほうがコスト的にも速度的にも有利になる。その時、意外と急速にIPv6への移行が起こるのかもしれない。

2016.01.04

IPv6のアクセスが10%に達する

Googleに対するアクセスの比率では、IPv6が (全体に対して) 2015年12月31日に10% に達した (1日あたり) らしい。アクセスは休日に高くなる傾向があり、スマートフォンなどの携帯デバイスが (米国で?) IPv6 を使用しているのがその原因らしい。年末のホリデーシーズンにアクセスの比率が高くなり、10%の大台に達したところである。

これは、10%のホストがIPv4を使用できないことを意味しているのではなく、10%のホストがネットワーク環境を含めて、(おそらく) IPv4とIPv6の両方を使用できることを意味している。現状ではIPv4でのアクセスしかできないサーバがあるため、IPv6のみのホストはほとんどないと考えられるからである。

5%を超えたのが2014年10月なので、15ヵ月で倍増したことになる。上限を100%としてロジスティック曲線にあてはめようとしたが、精度が出なかった。上限を20%とするとかなりの精度で実測値と一致する。これはIPv6のアクセスが20%を超えないことを意味するのか……ゴンペルツ曲線にあてはめると、上限が50%程度で比較的近い値になった。いずれにしても、現時点での未来予測は困難のようである。

2012.02.06

東アジアのサイトは奇数年に増える?

Googleによれば、Unicodeを使用したサイトが60%を超えたとのこと。

その事自体は別に驚きは無い。既に50%近いという報告がなされていて、別に遅いとも早いとも思わなかった。ただし、グラフを見ていて不思議な感覚を持った。

なぜ、JapaneseやChineseは奇数年が多いのだろう……

Chineseについては2006年以降で一貫してこの傾向が見られる。Japaneseは2010年以降だが、やはり同じ傾向である。よく見ると、ASCIIも2008年以降同じ傾向がある。Latin-1は逆に2007年以降奇数年に少ない傾向がある(その結果、1年置きにASCIIと2位が入れ替わっている)。件のUTF-8も2008年以降同じ傾向である。

いったい、なんでこんなことになっているのでしょう……

2011.10.23

Unicode固有文字とUnicode依存文字

最近、Webを見ていて、「Unicode固有文字」という用語が使われている場合がある事に気がついた。世界中にはUnicodeでしか文字コードとして表現できない文字(楔形文字など?)は存在するが、日本で使われている文字の大半はUnicodeに収録される以前から他のコード(Adobe-Japan1-6など)に収録されているので、Unicodeに「固有」であるという事はまずない。

「固有」は変という事からか、「Unicode依存文字」という言い方もあるようだ(これは、MS IMEの「環境依存文字 (unicode)」という表現から派生したのかもしれない)。しかし、先に述べたようにUnicodeに「依存」しない表記はほとんどの場合可能である。内部処理でUnicodeに依存しているのだと言うのであれば、WindowsにしてもMacintoshにしても現在では全ての文字がUnicocdeに依存している状態である。そもそもHTMLの規格レベルでUnicode(厳密にはUCS)に依存しているのであるから、Web上の文字は全てUnicode依存と言っても差し支えない。

という訳で、Unicocde固有(ないし、Unicode依存)文字の意味は自明ではない。定義が必要だが、意味が述べられているところでは「(シフト)JISに存在しない」文字が多かった(少数だが「EUCに存在しない文字」というのもあった)。しかし、シフトJISとはなんぞやというのは意外と難しい。そもそもJISと言ってもJIS X 0208なのか0213なのか不明である。シフトJISからは IANAの "Shift_JIS" を連想する(Shift_JISはJIS X 0208ベースである)が、シフトJISと言った場合はWindows-31jの事を意味している場合の方が多いであろう。EUCも複数あるが、IANAの "EUC-JP" であれば、JIS X 0208とJIS X 0212がベースである。

抽象的で良く解らないと思われるため、例として、以下の文字を含むか否かを考える。

  • 丸数字「①」
  • 森鷗外の「鷗」
  • 內田百閒の「閒」
  • アクセント符号付きのアルファベット「À」

Unicode固有文字の明確な定義がある訳ではないので、上記の文字に対して一貫した答えは出ない。JIS X 0208ベースであれば上記の文字は全て「Unicode固有」となる*1であろう。Windows-31jであれば、「鷗」と「À」だけが「Unicode固有」となるであろうし、EUC-JPであれば「①」と「閒」だけになるであろう。結局のところ、単に特定の実装(処理系)で表せない文字を「Unicode固有」等の言葉で表現しているものと思われる。「表せない文字」を定義するのは無理があるため、表せる文字の方を定義すべきである。

--------
*1: 厳密には包摂しているため表現可能な文字も存在するが、字体を区別する事はできない。

2008.08.24

正規等価(Javaの正規表現)

前回の記事で、CANON_EQ モード(正規等価モード)の挙動がよく解らない事について触れたが、「Java 正規表現アプリケーション」というサイトで正規等価モードの挙動が解った(Javaの正規表現と正規等価については前回の記事を参照いただきたい)。

java.util.regex の実装では、正規等価モードの場合パターンをコンパイル時に正規等価である様々な分解形や合成形に展開しておき、いずれかに一致すればマッチした事になる。「Java 正規表現アプリケーション」では NFC と NFD に分解されるとあるが、実際には正規形以外にも正規等価の形があれば展開される。今回も、貞廣知行氏の「使いこなそうユニコード」中の「Unicode正規化とは」で挙げられている例を使って説明する。

「e, U+0304 (MACRON), U+0301 (ACUTE), U+0323 (DOT BELOW)」という文字列(Javaのユニコードエスケープ表現では "e\u0304\u0301\u0323"、「e macron acute dot-below」という文字を表す)は、NFDが "e\u0323\u0304\u0301"、NFC が "\u1EB9\u0304\u0301" であるが他にも正規等価の関係にある下記のような文字列に展開される(元のパターンを含む)。

  • "e\u0304\u0301\u0323"
  • "\u0113\u0301\u0323"
  • "\u1E17\u0323"
  • "e\u0304\u0323\u0301"
  • "\u0113\u0323\u0301"
  • "e\u0323\u0304\0301" (NFD)
  • "\u1EB9\u0304\0301" (NFC)

ただし、正規等価の関係に無い "e\u0323\u0301\u0304" (「e acute macron dot-below」を表す)には展開されない。これで、正しい正規等価の判定が行われる事になる。パターン側のみを展開するため、判定対象の文字列は元のまま一切変更されない(そのため、前の記事の通り文字数も元のままのコードポイントによって数えられる)。

しかし、「Java 正規表現アプリケーション」にもある通り、正規等価モードにはバグがあるため残念ながら全ての場合に正しい正規等価の判定が行われる訳ではない。Fixしていない既知のバグは2種類である。

1つは、分解(または合成)される文字が2個以上連続すると Pattern#compile が例外を投げるというバグである(Bug ID: 6728861)。例えば、"ギガント" のように濁音が2個以上連続する場合は例外になる。

もう1つは、「合成除外文字」が決してマッチしないというバグである(Bug ID: 6736245)。このため、オングストローム(Å)のような記号や互換漢字は正規等価ではマッチしない(たとえ、自分自身であっても)。これは、パターンの展開時に行われる合成に正規合成用のテーブルを流用している事が原因と考えられる。

この他にも、正規等価モードには文字クラスの扱いが特殊であるという問題もある。文字クラスの範囲のどちらかの端に合成文字が含まれていた場合、範囲指定そのものが無効になり、両端の文字にのみマッチするようになる。具体的に言えば "[か-ご]" は本来 "[かがきぎくぐけげこご]" の意味になるが、正規等価モードで "[か-ご]" と記述した場合には、単に "(?:か|ご)" と記述した事と等価になる。この動作はバグにようにも見えるが、正規等価の定義「2つの文字列の正規分解が等しい」に照らして考えると、"[か-ご]" を正規分解した場合に意図しないパターンになる("[か-こ\u3099]"となり全ての濁音にヒットする)ため、規格上の動作は未定義と言うべきだと考える。

以上に挙げたように、正規等価モードには仕様上の疑問点やバグが残っている。確実な動作を狙うのであれば、正規等価モードを使用するのではなく、"(?:が|か\u3099)" のように明示的に展開するのが良いであろう。

2008.06.08

IRIの正規化(その2)

前稿では、IRINFC で正規化されていることが前提だと述べた。しかし、Unicode の正規化にはいくつかの問題があり、単純に正規化すれば済むというわけにはいかない。本稿では正規化の問題点を挙げた上で、実装例を元に扱い方を考察したい。

そもそも、Unicode の正規化とは同じ文字を示す複数のコード列を同一のコード列にまとめる事で扱いを容易にするための処理である(例を挙げると、「か」+濁点のコードを「が」に変換する)。IRI は URI と同じく印刷されたり手書きのメモに利用される事も考えられるため、視覚的に同じ IRI を表すコード列が複数あると識別子として不十分なだけでなく、リソースへのアクセスを保証できない。

そのため、IRI は視覚的に同じ文字を示すコード列を一意に特定することを目的に NFC という正規化が行われていることが推奨される訳だが、「視覚的に同じ」という言葉だけをとっても実装の違いや文化的な背景の影響を受け一概には決まらない場合がある。世界共通の文字コードを標榜する Unicode は正規化の規則も世界共通である必要がある訳だが、多様な文化を背景とした多数の実装が存在する事を考えれば正規化の規則を定めるだけでも容易ではない事が解るだろう。

それでは具体的にどういう問題があるかだが、日本語での利用に絞って問題を考える(外国語の問題については筆者は堪能でないため把握できない)。ご容赦いただきたい。

Unicode の正規化の詳細と問題は、貞廣知行氏の「使いこなそうユニコード」中の「Unicode正規化とは」を参考にさせていただいた。

正規化(NFC) の具体的例を以下に示す。

記号

いくつかの記号は正規化の対象となっている。
オングストローム: Å (U+212B) は、上リング付き大文字A: Å (U+00C5) に正規化される。
ただし、マイクロ記号: µ (U+00B5) は、ギリシャ文字ミュー: μ (U+03BC) には正規化されない(NFCの場合)。

かな

平仮名または片仮名と合成可能の濁点または半濁点が連続する場合、合字があれば正規化される。
「か」 (U+304B) と濁点 (U+3099) が連続している場合は「が」(U+304C) に正規化される。

漢字

互換漢字と呼ばれる漢字群は統合漢字と呼ばれる漢字に正規化される。
互換漢字「神」(U+FA19) は、統合漢字「神」(U+795E)に正規化される。

見ていただければ解るように、必ずしも視覚的に同じように見える文字が正規化される訳ではない。そもそも、あるコード列が同じに見えるかどうかは実装によるので一概には決まらないのである。正規化で同一になるコード列(正規等価と呼ぶ)は視覚的に区別できない事が望ましいと言えるが、互換漢字「神」と統合漢字「神」のように元々視覚的に異なっている文字と思われる例もある。

正規化の規格は今まで述べた通りだが、実装はどうなっているであろうか。IRI がリソースの名前や所在を表している場合、もし異なる IRI が正規化によって同じ IRI になればリソースの区別がつかない。同じリソースと看做すか別のリソースと看做すかは実装によって変わる事になる。仮にブラウザ側の実装が正規等価の IRI を同一のリソースと看做して区別しないとすると、もしサーバ側で区別されていた場合には本来存在するリソースにアクセスできない事態になる(後述するようにサーバ側に正規化の規定はない)。これは前稿で述べた「Unicodeで表現された IRI はそれ以上正規化してはならない」規則の理由に他ならない。

一方、サーバ側では区別するかしないかを独自に決める事ができる。サーバが扱うのは URI であって IRI を直接扱う訳ではない。URI には正規化の規定はないためサーバでの扱いは決まっていない(しかし、ブラウザは IRI を等価な URI に変換してサーバに送るため、ブラウザの利用者からはサーバが間接的に IRI を扱っているように見える)。具体的に実装を見てみよう。

Mac OS X

Mac OS XはWWWサーバの機能を持っている(実装はApache)。Apache は通常 URI の正規化を行う事はないが、Mac OS X のファイルシステムはファイル名について独自の正規化を行う。従ってファイルを指す URI について Mac OS X は正規化して扱う事になる。独自正規化のルールは簡単に言えば一部の記号と漢字を正規化の対象から外すというもので、独自ルールによって互換漢字「神」(U+FA19) と、統合漢字「神」(U+795E)は区別される。

Linux

Linux 上に Apache を載せた場合、Apache によって URI の正規化を行わなかったとすると、Linux のファイルシステム(UFS)はファイル名について正規化を行う事はないため、URI (IRI) はいっさい正規化されない(区別される)。ちなみに UFS が UTF-8 で構築されていなかった場合は URI はファイルシステムの文字コードで表現される。

Wikipedia

Wikipediaの URI の扱いを記述した文書は発見できなかったが実験の結果では NFC による正規化が行われているようである。具体的には、NFC でない(IRIをパーセントエンコーディングした)URI が与えられた場合、NFC に変換後の URI にリダイレクトされる。すなわち、互換漢字「神」(U+FA19) の項目を参照した場合には統合漢字「神」(U+795E)の項目を表す URI にリダイレクトされる。

WWW サーバによって NFC で正規化するもの、独自の正規化を内部的に行うもの、まったく正規化を行わないものがある。ブラウザはサーバの実装を知らないので正規化せずに透過的に扱うしか無いのは前述した通りであるが、IRI が Unicode 以外の文字コードで記述されていた場合や印刷されていた場合には NFC で正規化されていたものとして扱う事を規格は要求している。たとえ規格で決まっていなかったとしても、印刷された IRI の文字が正確にどのコードポイントかを判断するのは時として困難である。安全性を考えれば、IRI としてパーセントエンコーディングを行わずに文字で表現する場合は NFC の場合のみとし、NFC でない(NFC に変換できない)場合、特殊な文字や記号を含む場合はパーセントエンコーディングのママ(URIとして)扱うのが確実である。

2008.05.24

IRIの正規化

国際化URI (IRI) で触れていなかった問題に正規化の問題がある(IRIの詳細は前回の稿を参照いただきたい)。UCS (Unicode) は同じ文字を表す複数のコード列が存在するため、識別子として用いるためには正規化の必要がある。

IRI の規格である RFC 3987 では、正規化を次のように扱っている。

  • 印刷された IRI は NFC*1 で正規化されていると看做す (3.1)

  • Unicode 以外の文字コードで表されている場合は NFC で正規化して UTF-8 に変換する (3.1)

  • 一般に IRI は NFC で正規化されている事が推奨される (5.3.2.2)

扱いは明確なのだが、奥歯に物が挟まった感がある。実は、次のような決まりもある。

  • Unicodeで表現されたIRIは(それ以上)正規化してはならない (3.1)

単純に、「IRIは常にNFCで正規化されなければならない」と決めれば良いようなものだが、規格はそうなっていない。理由は明記されていないが次のような事情があったと推測される。

IRI は新しいプロトコル要素だが URI と相互に変換することによって運用される。具体例を挙げると、ブラウザは IRI を URI に変換した上で Web サーバに送らなければならない(HTTP上では IRI は送信する事ができない)。また、利便性のため、ブラウザは URI を IRI に変換して表示する事も許されている。つまり、IRI は URI の表現方法(ビュー)の一つと考える事ができる。

IRI が URI のビューに過ぎないとすれば、既存の URI を無視して定める事はできない。URI に対しては文字コードすら決まっておらず、当然、正規化の規定はない。つまり、URI はパーセントエンコーディングさえ行えば、まったく正規化しまいが NFD*2 で正規化しようが(Shift_JISで*3表そうが)自由という事になる。IRI は URI との往復変換で元に戻る事を保証する必要があるため、IRI だけで正規化を行う事ができない。つまり、既存の URI との互換のため正規化が義務づけられなかったと考えられる。

つまり、UTF-8 で表した(文字列をパーセントエンコーディングした)URI は IRI として us-ascii 以外の文字で表現しても良く、そのまま URI に変換した場合は元に戻る事が保証されている。しかし、その IRI を印刷した瞬間(あるいは Unicode 以外の文字コードに変換した瞬間)に NFC で正規化した事になり、元の URI が NFC で正規化されていなかった(ものをパーセントエンコーディングした)場合は元に戻る事はない。IRI を安全に印刷できるのは NFC の場合のみという事になる。

いずれにしても、IRI を使用する(記述する)場合は NFC で正規化するものだと思えば、ほぼ間違いないであろう*4

--------
*1: できるだけ短くなるように正規化した形式(正確には正規分解したのちに正規合成した形式)。
*2: 全ての合成文字を分解(正規分解)した形式。
*3: IRI は UTF-8 でエンコードされている事が前提である。従って Shift_JIS でエンコードされた URI はIRI に変換してもパーセントエンコーディングのママとなる。
*4: Unicodeの正規化そのものに関しては問題が残る。その件は続稿で考えたい。

2008.05.06

コトノコの広辞苑用外字ファイル

私が普段利用している、広辞苑第五版 CD-ROM版と EPWING用ビューアのコトノコ用の外字定義ファイルを作成しました。

広辞苑第五版 外字ファイル (コトノコ用)

EBWin用の広辞苑の外字定義ファイル(本文に現れる全角外字1675個を中心に定義したもの)と、NDTPD辞書クライアント用の外字定義ファイル(記号を中心に定義したもの)をマージして作成したものです。それぞれの作者に感謝します。

あくまでも個人的な目的のために作成したファイルですが、どなたかの役に立つかもしれないので公開します。文字の割当は元ファイルを利用していますが、マージしたファイルの誤りの責任は私にあります。

(広辞苑第六版でも使用できるという情報もあるが確認はしていない)

2007.03.10

Javaの正規表現

SUNの標準の正規表現ライブラリであるjava.util.regexパッケージについて調べてみた。java.util.regexは非決定性有限オートマトン(NFA)に基づく(Javaには多数の正規表現パッケージがあるが決定性有限オートマトン(DFA)を用いた実装はないらしい)。

いきなりですが、ここで問題です。次のJavaの式の値は何でしょう(ちょっと引っ掛けかも。ヒントはサロゲートペア)。

  • "\uD835\uDD21".matches(".*[\\uD834\\uDD21]+.*")

ちょっといやらしい例だったので、もっと素直な例を。以下のJavaの式の値は何か(String#matchesは全体一致である事に注意)

  • "\uD834\uDD21".matches(".")

解説、\uD834\uDD21 は Java の Unicode エスケープでコードポイント U+1D121(一文字)を表す(サロゲートペア)。Unicode の正規表現での扱いはコードポイント単位であることが規定されている
U+1D121 のカテゴリは OTHER_SYMBOL (So)。ちなみに Java 1.4 と 1.5 では扱いが異なる(はず)*1

基本を確認したところで再び問題。以下のJavaの式の値はそれぞれどうなるか。(最後の式の結果は論理的には導けません。あしからず)

  • "\uD834\uDD21".matches("\\p{So}")
  • "\uD834\uDD21".matches("\\p{Cs}+")
  • "\uD834".matches("\\p{Cs}+")

一応、正解(というか実際の動作結果)を記しておく。結果は全てbooleanである。true を t、false を fで記す。出題順に / で区切り、複数の式がある場合は連続して記述する。

f/t/tft(Java version 1.5.0_09 での結果)


ちなみに、サロゲートペアは以下のように範囲で使用することもできる。

"\uD834\uDD22".matches("[\uD834\uDD21-\uD834\uDD24]+")

評価結果は true となる。
これはもちらん、

"\uD834\uDD22".matches("[\\uD834\\uDD21-\uD834\uDD24]+")

と書いても同じである。ところが、正規表現を

"[\\uD834\\uDD21-\\uD834\\uDD24]+"

と記述した場合、java.util.regex.PatternSyntaxException: Illegal character range が発生する。どうやら正規表現の文字クラス内のUnicodeエスケープの処理にバグがあるようである。

--------
*1: Java 1.4 (J2SE 1.4) はUnicode 3.0に準拠している。Unicode 3.0にはサロゲートペアの規定はあるものの、実際にBMP以外の領域(サロゲートペアを必要とする範囲)に文字は割り当てられていない。このため、J2SE 1.4のサロゲートペアへの対応は最小限にとどまっており、実質的には使えないに等しい。サロゲートペアに対応したのは Java 1.5 (J2SE 5.0)以降である。


ここまで、主にUnicodeのコードポイントに着目してJavaの正規表現(java.lang.regex)について調べてみた。しかし、Unicodeは単にコードポイントが決まれば文字が決まるような単純な体系ではない。同じ文字を表現する多くのコード列が存在する。同じものが複数あると不便なので「正規形(Normalization Forms)」という形が決められている(正規形と正規表現(regular expression)、英語だと全然違うのに日本語だと混乱しますね)。

ところが、Unicodeでは正規形も複数決められているのである(一体、どこが "Uni" code なんだ?という感じだが)。NFD, NFC, NFKD, NFKC の4通りある。NFDが最も基本的なもので、全ての「合成文字」を「分解」した形になる。と言っても解りにくいので例を挙げると、「が(U+304C)」という文字は「か(U+304B)+結合用濁点(U+3099)」に分解される。

さて、java.lang.regexでは CANON_EQ をサポートしている。CANON_EQ とは、Canonical Equivalents の事でJavaDocの日本語版では標準等価と訳しているが、一般的ではないようなのでここではCANON_EQ とする。問題は CANON_EQ の定義であるが、Unicode Technical Standard #18 に規定されている。しかし、規定をみても具体的な方法までは決められていない。Javaの正規表現では、「2 つの文字の完全な標準分解がマッチした場合に限り、それらの文字がマッチするとみなされます」とあるので、NFDに基づいている事が解る。

ところが、実際にjava.util.regexを使ってみると、どうも不思議な動きをする。具体的には合成文字を与えた場合と、結合文字列(基底文字と1以上の結合文字を続けた列)を与えた場合で動作が変わるのである。たとえば、合成文字を与えた場合は1文字と数えるのに対して結合文字列を与えた場合は複数文字と数えたり、合成文字を与えてもマッチしなかったパターンに結合文字列を与えればマッチする事がある。単純にNFDに変換してから比較している訳ではないらしい。

誤解していたかもしれないので補足しておく。「2 つの文字の完全な標準分解がマッチした場合に限り、それらの文字がマッチするとみなされます」は Canonical Equivalents の定義(文字列のNFDが等しい)を述べているに過ぎない。つまりJavaの「実装」がNFDに基づいているという意味はない。と読める(文字単位の比較である事は述べているように見えるが、文字=コードポイントとすると文字単位の比較がCannonicalというのは矛盾が残る)。JDK 6のドキュメントでは用語が「正規等価」、「正規分解」に改められている。


結局のところ、java.util.regex はコードポイント単位で処理するのが大原則であるという事につきるらしい。したがって、「が」は一文字だし、「か+濁点」は2文字と数える。「が」は「か」や「濁点」にはマッチしないが、「か+濁点」は「か」にも「濁点」にもマッチする。

従って、CANON_EQ の有無は、この例では「が」が「か+濁点」にマッチするかしないか、「か+濁点」が「が」にマッチするかしないかの違いだけである。

利用者から見れば、画面上に表示される文字(Grapheme Clusterと呼ぶらしい)単位で処理される事を期待しそうだが、java.util.regex はそうなっていない。あくまでもコードポイント単位である。従って同じ文字(Grapheme Cluster)であってもコードポイントの表現の方法によって正規表現のマッチの結果が変わる(たとえ CANON_EQ でも)。

Unicode Technical Standard #18 によればコードポイント単位の処理は誤りではない。例えばドット (.) は1つのコードポイントにマッチするのが標準らしい。Grapheme Cluster にマッチする記号の例としては \X が挙げられている(Javaでは実装されていない)。正規化と CANON_EQ はあくまでも別物と考えるべきであろう。

これはJavaの処理の場合であって、Unicode Technical Standard #18 ではあらかじめ正規化してから処理する方法も認めている。この場合は処理単位のコードポイントが元の文字列と変わる事になる。その代わり同じ正規形を持つ文字列であれば同じマッチ結果を持つことが保証される。

2005.01.28

国際化URI

IETF と W3C は URI の規格を改正し、国際化の方式を盛り込んだ。新しい規格は RFC 3986 "URI Generic Syntax" と RFC 3987 "Internationalized Resource Identifiers (IRIs)" の二重の構造になっている。

まず、RFC 3986 は従来の URI と互換を持つ形で様々な文字コード(符号化方法)の取り扱い方を整理する事で国際化を行っている(従来は厳密に言えば us-ascii 以外の文字コードを扱う方法は規定されていなかった)。手法としては、現在の(混乱したとも言える)状況をそのまま認めるという方法である。

Implementers have to be aware that there are multiple character encodings involved in the production and transmission of URIs: local name and data encoding, public interface encoding, URI character encoding, data format encoding, and protocol encoding.

"local name"(ファイル名など)を URI に変換する方法は決まっている訳ではない。URI(の path 部分)はサーバのみで解釈するため、特にクライアントと処理方法を決めておく必要はないからである。そこで、

For example, consider an information service that provides data, stored locally using an EBCDIC-based file system, to clients on the Internet through an HTTP server. When an author creates a file with the name "Laguna Beach" on that file system, the "http" URI corresponding to that resource is expected to contain the meaningful string "Laguna%20Beach". If, however, that server produces URIs by using an overly simplistic raw octet mapping, then the result would be a URI containing "%D3%81%87%A4%95%81@%C2%85%81%83%88". An internal transcoding interface fixes this problem by transcoding the local name to a superset of US-ASCII prior to producing the URI. Naturally, proper interpretation of an incoming URI on such an interface requires that percent-encoded octets be decoded (e.g., "%20" to SP) before the reverse transcoding is applied to obtain the local name.

EBCDICをそのまま(パーセントエンコーディングして)URI にする事は極端としても、US-ASCIIと互換の文字コードなら自由に使って良いと言うことになる。これは現状の Shift_JIS や EUC のファイル名をそのままパーセントエンコーディングして URI としている事の追認である。

しかし、全てサーバ側だけの問題なので何も決めなくて良いという訳にはいかない。フォーム中に記された文字をどのように URI として符号化するかはクライアント側の責任であり、その URI を解釈するのはサーバ側の責任になるため、フォーム中の文字列の符号化については決めておかなくてはならない。もちろん、規格では URI のスキームなどによって符号化の扱いを決める事も認めている

In some cases, the internal interface between a URI component and the identifying data that it has been crafted to represent is much less direct than a character encoding translation. For example, portions of a URI might reflect a query on non-ASCII data, or numeric coordinates on a map. Likewise, a URI scheme may define components with additional encoding requirements that are applied prior to forming the component and producing the URI.

が、RFC 3986 はあくまでも URI の一般規則を示しているに過ぎず、また、現状の追認以上の何かを行っているわけでもない。既存の HTTP などのスキームの解釈を変えているわけではないので、HTTP でのフォーム中の文字の扱いを具体的に規定しているなどという事はない。「us-ascii 以外の符号を URI 中に(パーセントエンコーディングした上で)含めても良い」というお墨付きが得られただけである。

ところで、RFC 3986 では初めて fragment 識別子が URI の一部である事が認められた*1

URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

これで晴れて # つきの URI も URI (または URL)と呼ぶ事ができるようになった(個人的にはこれが一番嬉しいかもしれない)。

次はいよいよ国際化の主役である IRI についてだが、RFC 3987 で規定されている。

This document defines a new protocol element called Internationalized Resource Identifier (IRI) by extending the syntax of URIs to a much wider repertoire of characters. It also defines "internationalized" versions corresponding to other constructs from [RFC3986], such as URI references.

つまり、URI は現状のママとしておいて、国際バージョンの URI を新しく作ろうという趣旨らしい(どうせ、そのうち IRI も URI もまとめて URL とか呼びだすのだろうが、それも策定者の狙いのうちなのかもしれない)。IRI の手法は(国際化ドメインの手法と似ていて)様々な文字で表現された IRI と、それと「等価」と決められた URI (us-asciiのみで表現される)の間の相互変換を可能にするという事である。

ではその変換はどうするのかと言うと、かなり単純な方法である。まず IRI の文字を全て UTF-8 に変換する(変換が不要なら何もしない)。その上でパーセントエンコーディングを行えば等価な URI が得られる。これくらいの実装なら現在のブラウザは既に行っているだろう(IRI のうちドメイン名の部分は変換方法が異なる事に注意)。逆の変換を行えば URI から IRI が得られる。

ただし、規格ではあらゆるケースに対応しなければならない。URI では必ずしも UTF-8 という文字コードが決められていた訳ではない。従って UTF-8 ではあり得ないバイト列のパーセントエンコーディングを含む URI も存在する。その場合の扱いは「UTF-8 ではあり得ないバイト列になる部分はパーセントエンコーディングのママ残す」という方法である。従って、IRIであってもパーセントエンコーディングは存在する*2

厳密に言えば、UTF-8 のように見えても UTF-8 でない文字は存在する。例えば ISO-8859-1 で表現された 2 文字が UTF-8 で表現された 1文字と同じバイト列であったり、Shift_JIS で表現した 3文字が UTF-8 で表現した 2 文字と同じバイト列であったりすることがあり得る。この辺りは往復変換で同じものになるのであれば気にしないというところであろう*3

規格では変換と IRI 同士の比較(何を同じ IRI とするか)について詳細が決められているがここでは割愛する。ただし、IRI は URI との関係が決められているだけなので例えば HTTPでのフォームの扱いなどが明確に定められた訳ではない。(他との整合性において)「UTF-8 で符号化する」のが示唆されるが、別に他の扱いを禁止してはいない。フォームの扱いは別の規格書を待たなくてはならない(この部分が一番欲しかった部分なのであるが)。

--------
*1: 今まで fragment 識別子は継子扱いされてきた部分である(以前の稿を参照されたい)。
*2: UTF-8 で表現可能な文字であっても URI 上で特別な意味を持つ文字('/' や '%' 自身など)や URI に含めることが禁止されている文字(空白など)はパーセントエンコーディングされる。
*3: 実害がない訳ではない。元々 Shift_JIS であった 3 文字を UTF-8 と見なして 2 文字とした場合、その文字が表示できる保証はない。従って化けて表示されたり印刷されたりする場合が考えられる。

最近のトラックバック

無料ブログはココログ

Googleアナリティクス

  • Googleアナリティクス