目次へ戻る §19へ戻る §21へ進む まとめ(英) ダウンロード 使用法 内部実装

20. UTF-8 Cygwin

2006/3/31 - 2007/12/13 (鈴)

20.1 はじめに

逆説的に聞こえるかもしれませんが, Windows XP のネイティブな日本語文字コードは Shift_JIS (ないしその変種) ではありません。この事実は普段は隠されていますが, 簡単な操作で表に出せます。

コントロールパネルの「地域と言語のオプション」→「詳細設定」タブ → 「Unicode 対応でないプログラムの言語」を「日本語」以外 (たとえば「英語 (米国)」) にして「OK」とします。

このときでもエクスプローラ等でファイル名やフォルダ名に日本語が使えます。 もちろん日本語 IME も使えます。 メモ帳で日本語文を書くことも,保存することもできます。 メニューが英語になる点を除けば,英語版 Windows XP でも事情は同じです。 初期状態では容量節約のためフォントや IME が入っていないことが ありますが,そのときは コントロールパネルの "Regional and Language Option" → "Languages" → "Install files for East Asian languages" にチェックを入れます。

どの国向けの設定であっても,Windows XP はネイティブに日本語を扱えます。 ただし,それは Shift_JIS ではありません。Unicode です。

日本語に限らず,立場はどの国語も同じです。 日本語 Windows であってもファイルやフォルダの名前に Shift JIS 表外の各国語文字を Unicode で使えます。 その結果,たとえばフォルダ名をもとにディレクトリをトラバースするとき, 一般に Unicode でフォルダ名を扱う必要があります。 Windows API から取得したハンドルをもとにトラバースするタイプの Windows アプリケーションならばともかく, 現状,Unicode 対応でない Cygwin 上の Unix シェルにとってこれは致命的問題です。 ファイルシステムに可能な文字集合の小さなサブセット (日本語環境の場合,Shift JIS の一変種) しか, 今の Cygwin には扱えないからです。 たとえば, 人気の音楽アプリケーションである Apple 社の iTunes は,アーティストやアルバムと同名のフォルダ, 曲と同名のファイルを Unicode で作成しますが, Cygwin からこれらを扱うことは一般にはできません。 "Kodály Quartet" とか "09 Prélude.m4a" などの フォルダ名,ファイル名を考えてみてください。

20.2 Cygwin の UTF-8 化

C 言語からみた文字列の扱いを変えないようにして, プログラムのほとんどを温存したままこの問題を解決する方法が, 今からおよそ 14 年前に Plan9 のために発明されています。 それが Unicode の ASCII 互換の表現方法である UTF-8 です (詳しくは Wikipedia 等を足がかりに確認してください)。 この方法は Cygwin にも応用できます。 Unicode で格納されているパス名を Windows と Cygwin の境界面で UTF-8 に等価変換すれば, Cygwin プログラムを事実上 Unicode 対応にできます。

具体的なソフトウェアの構成としては,Unix API を使う Cygwin プログラムと, オペレーティングシステムである Windows の橋渡しをする cygwin1.dll を改造し, その Windows 境界面に UTF-8 ⇔ Unicode 変換層を設けます。 *1

直接 Windows API を呼び出している少数の Cygwin プログラムには個別に対応が必要ですが, ほとんどの Cygwin プログラムは,再コンパイルすることなく, そのまま Unicode のファイル名,フォルダ名を扱えるようになります。

ここでは以下,筆者によるこのような試みの一つ UTF-8 Cygwin を述べます。

UTF-8 Cygwin の実体である改造版 cygwin1.dll は, ファイルやフォルダのパス名と,コンソール入出力を UTF-8 にします。 その実現のため Windows API に対し UTF-8 ⇔ Unicode 変換をします。 いわゆる従来の「日本語」文字に限定されず, 本来 Windows で利用可能な広範な文字を使うことができます。 ファイルの内容については今までどおりです。 Unix の伝統に従い透過的に扱います。 しかしながら,今まで Cygwin 経由のコンソール入出力で利用可能な文字は (日本語環境では) Shift JIS の一変種で表現可能な範囲に限定されていましたから, UTF-8 による多様なテキスト表現はいわば絵にかいた餅でした。 UTF-8 Cygwin では Unicode 系 Windows API を使うことでこの制限をなくしますから, ファイル内容にも普通の意味で UTF-8 を利用できます (下図で Latin-3 文字と日本語全角文字が 同時に表示されていることに注目してください)。

なお,日本語特有の問題として,現状の Cygwin には Shift JIS の「表」や「ソ」など,第2バイトが 「\」文字 ('\x5C') と一致するマルチバイト文字を含んだファイル名, フォルダ名をうまく扱えない不具合がありますが, これも UTF-8 の利用により自然に解消します。 UTF-8 では ASCII 文字 ('\0' - '\x7F') が他のマルチバイト文字の一部になることがないからです。

なお,この試みでは,従来のデータ資産との相互運用性の便宜のため, UTF-8 と矛盾しない範囲で従来の文字コードを使えるようにします。 日本語 Windows では,これはたまたま Shift JIS になりますが, 幸運にも UTF-8 と Shift JIS は ASCII および第1水準漢字という使用頻度の高い文字について矛盾しないため, 実用上ほぼ十分な程度,透過的に従来の Shift JIS も使えます。 ただし,両者が矛盾するときは UTF-8 を優先します。 UTF-8 Cygwin に Shift JIS 専用のコードは一切含めません。 実装は一般的,普遍的に行います。 日本語に対する特別処理は,もし行うとしても,あくまで Unicode の枠内で行います。

20.3 内容

長くなりますから,本文にあたる内容を下記のように分けます。

この試みは2月ごろ Cygwin に対する実験的な改造として始まりました。 3月末に一応の完成をみた後,この夏まで日常的に利用してきましたが, 後述するように libc 層での locale のサポートの欠如による不便のほかは, 特に不具合はありませんでした (たとえば vim によるスクリーンエディットも問題なくできます)。 十分に実用できると思います。

プログラムのライセンスはオリジナルと同じとします。自由にお使いください。

20.4 関連する労作との比較

関連する労作として 2002年 7月 3日に cygwin のメーリングリストで C. January 氏が発表した UTF8 patch を挙げることができます。 これと比較したここでの試みの主な特徴*2は次のとおりです。

第1の項目の利点は,この春から夏にかけての Cygwin 1.5.19 から 1.5.20, 1.5.21 の二度のバージョンアップへの迅速な追従で実証されたといってよいでしょう。

日常の使用では第2と第3の項目が重要です。 コンソールを UTF-8 化することで,別に UTF-8 端末を用意する必要がなくなります。 また,デフォルトのコードページにフォールバックすることで, (たまたま日本語環境では) Shift JIS で書かれたテキストファイルを透過的にコンソールから閲覧できます。 また,Shift JIS によるシェルスクリプト (日本語フォルダを mkdir したり,ln -s したりする) も実行できます。

ですから, もしも人の PC の Cygwin をこっそり本実装にすげかえたとしても, しばらくは気づかれないはずです。 なぜか "A-表" のような名前のフォルダにもごく自然に cd できる, といったところで おや? こんなこと出来たっけ? と不思議がられる,というわけです :-)。 さらにそれが単に Shift JIS でないばかりでなく,Unicode 系 Windows API による本物の UTF-8 であるという事実を把握するには, また時間がかかるかもしれません。*3

20.5 おわりに

ここでの試みでは, Cygwin と Windows API の境界面で UTF-8 ⇔ Unicode 変換を行うように Cygwin の基本ライブラリ cygwin1.dll を改造しました。 その結果,プログラムのバイナリ互換性を維持しつつ, Windows のすべてのファイル名,フォルダ名をほとんどの Cygwin プログラムから扱えるようにすることに成功しました。 UTF-8 ⇒ Unicode 変換の失敗時にデフォルトのコードページで再解釈することで, (少なくとも日本においては実用的に) 従来のデータも透過的に扱えるようにしました。

環境変数の取り扱い, libc 内の locale と多バイト関数の UTF-8 対応, Windows API を直接び出しているプログラムへの対応が,残された主な課題です。 ただし, Windows API を直接呼び出しているプログラムのうち,よく使われる cygstart (§19.2 参照) は一応対策済みです。 全般的な使い勝手としては, locale のサポートがない UTF-8 化 Unix 類ですから, ちょうど 2000 年ごろの BeOS R5 や 2002 年ごろの Mac OS X 10.2 *4 と同じような未完成感があります。 libc 実装の対応が最も重要で手のかかる課題になるでしょう。 glibc (のサブセット) を載せるなどの荒技が案外,最も現実的なのかもしれません。


脚注

*1 Cygwin は元々内部で POSIX パス名と Windows パス名の変換をしています。 この事実だけをとりあげて考えると, UTF-8 ⇔ Unicode 変換を, POSIX ⇔ Windows のパス名変換とともに行うのが自然に思えます。 これと比べると,ここでの方法のように UTF-8 ⇔ Unicode 変換を Windows API 呼出しの箇所で行うことは二度手間で非効率そうです。 しかし,今の Cygwin 実装では,前者の方法での UTF-8 化はほぼ不可能です。 事実上,現時点では空想的といってもよいでしょう。

実際,3月はじめごろ,前者の方法も試しましたが, 完成の見通しが立たず放棄しました。 今の Cygwin 実装は, POSIX パス名と Windows パス名用のデータおよび関数が厳密に分離しておらず, 単純に一方を Unicode とすることは困難です。 加えて,早い段階で Unicode 文字列に変換した場合, それを操作するための一群の基本関数を完備する必要がありますが, 今の Cygwin にその十分なサポートはありません。 新規にパス名処理一式を作り出すに匹敵する作業が必要です。 もし完成できたとしても,大量の新規コード導入のため, 基本ソフトとしてすぐに信頼できるものにはならなかったでしょう。

いずれにせよ,Unicode と POSIX 互換性を両立させようとすると, 一度は変換が必要ですから, 効率の差が開くのは Unicode パス名を何度も再利用する場合に限られます (本実装は,API 呼出しに伴う変換用作業領域を, スタック上に (可変長) 配列として高速に割り付けています)。 そもそも,ファイル内容に比較してパス名はデータとして十分に少量ですから, パス名の UTF-8 ⇔ Unicode 変換のコストはどのみち事実上無視できます (この言明の妥当性は本実装を試用して御確認ください)。

寄与の少ない効率向上のために困難な方法をとることは合理的ではありません。 ここでの方法は現実解として妥当であるといって,おそらく差し支えないでしょう。 さらにいえば, この方法は ANSI 系 Windows API を仕様を維持したまま (UTF-8 ⇔ Unicode 変換) + (Unicode 系 Winodws API 呼出し) にすげかえますから, もしも将来,何らかの賢い方法ないし多大な努力により, 直接 Unicode 系 Winodws API を呼び出すように Cygwin 本体が改訂されたとしても, あるいは ANSI 系 Windows API が直接 UTF-8 を受理するように Windows 本体が改訂されたとしても円滑に移行できます。

*2 これらの特徴は C. January 氏の労作を踏まえてのものです, と言えたら, オープンソースにおける教科書的な技術発展の一事例になれたかもしれませんが, 実はそうではありません。 おおよその実装が完成したのが春分の日, 氏のパッチを発見したのが 3月 23日未明でした。 発見したときは車輪の再発明をしてしまったかと思いましたが, よく読むと,結果的に努力に無駄がなかったようで安堵した次第です。

*3 ごく最近でも実際にそのような方がいました。 Cygwin 内部で ANSI 系 Windows API による Shift JIS 文字列と UTF-8 との相互変換をしているにすぎないと誤解している模様でした。
いわゆる「\」文字問題を解決するためだけならば, そのような見せかけの UTF-8 化も解としてあり得るでしょう。 しかし,その方法では,このページのスクリーンショットに示すような他国語文字と 日本語文字の混在はできませんし, UTF-8 Cygwin 作成の直接の動機となったディレクトリ・トラバースの問題も 解決しません。 逆にいえば,この誤解は,「\」文字問題ひとつに 長年いかに人々がわずらわされているかを示す悲しい証拠といえるでしょう。 Shift JIS の悲劇です。

*4 いわば異端の新興 Unix 類ばかりを例示しましたが,普通の Unix 類は, EUC-JP による日本語処理 → locale の採用 → (新参の) UTF-8 への対応, という発展経路を経てきていることに注意してください。 きょうび新しく OS を設計するとして,多少とも国際対応を考慮すれば, (UTF-8 に関する各種支援機能は後まわしにするとしても) 文字データにはさしあたり UTF-8 を採用するのが正着ですが, 例示の Unix 類はその「さしあたり」の段階だったわけです。 実用上の観点からはシェルの行編集機能が UTF-8 対応かどうかが重要ですが, 現在の bash は locale と多バイト関数を基礎として対応しており, 2003 年秋の Mac OS X 10.3 以降で行編集の UTF-8 対応が実現されています (ただし環境変数の設定が必要です。詳細は別の機会に述べます)。

目次へ戻る §19へ戻る まとめ(英) ダウンロード 使用法 内部実装

Copyright (c) 2006 Oki Software Co., Ltd.