The Unicode HOWTO: あなたのプログラムで Unicode を扱えるようにする

次のページ 前のページ 目次へ

## 6. あなたのプログラムで Unicode を扱えるようにする

## 6.1 C/C++

C 言語の ``char`' 型は 8 ビットですが、 これは扱える最小のサイズだからです。

### 通常のテキストハンドリング

1995 年に ISO/ANSI C standard に追加された修正条項に記述されているのは、 "wide character/ワイド文字" 型である ``wchar_t`'と、 `<string.h>` および `<ctype.h>` (それぞれ `<wchar.h>` および `<wctype.h>` で宣言されてい ます)にある一連の関数と、``char *`' と ``wchar_t *`' 間の 一連の 変換関数 (`<stdlib.h>` で宣言されています)です。

これらの API の良いリファレンス

- GNU libc-2.1 マニュアルの 4 章 "Character Handling" と 6 章 "Character Set Handling"
- マニュアル man-mbswcs.tar.gz、現在は ftp://ftp.win.tue.nl/pub/linux-local/manpages/man-pages-1.28.tar.gz に含まれています。
- OpenGroup のイントロダクション http://www.unix-systems.org/version2/whatsnew/login_mse.html
- OpenGroup の Single Unix specification http://www.UNIX-systems.org/online.html
- ISO/IEC 9899:1999 (ISO C 99) standard
  採用(adopted)前の n2794 と呼ばれている最新のドラフトは ftp://ftp.csn.net/DMK/sc22wg14/review/ もしくは http://java-tutor.com/docs/c/
- Clive Feather のイントロダクション http://www.lysator.liu.se/c/na1.html
- the Dinkumware C library リファレンス http://www.dinkumware.com/htm_cl/

これらの API を使う利点

- ベンダーに依存しない標準です。
- 関数はユーザーのロケールに応じた正しい動作をします。全てのプログラムで `setlocale(LC_ALL,"");` を呼びだす必要があります。

これらの API の欠点

- 呼びだし毎に使用する内部的な状態フラグを保持しているため、いくつかの関数は マルチスレッドセーフではありません。
- どのような時にでも使用できるというロケールデータタイプはありません。つま りこれらの API は、1 つ以上のロケールや文字セットを同時に使用しなければ ならない時には、うまく動作しません。
- 多くの OS で、これらの API のサポートは満足いくものではありません。

### 可搬性(Portability)について

``wchar_t`' が Unicode で、マルチバイトの文字列 ``char *`' が UTF-8 でエンコードされているかどうかはプラットフォームや、時にはロカー ルにも依存しています。

Single Unix specification では ``wchar_t`' 型についてこのように述べられています：

> プロセスの全てのワイド文字コードは、固定長のビットで構成されている。これ に対して文字(character)を構成するバイト数は可変である。文字を表現するバ イト(あるいはバイトの並び)は、ワイド文字コードとして表現することもできる。 つまりワイド文字コードにより、固定長でテキストデータを扱うことができる。 全ビットがゼロのワイド文字コードはヌルのワイド文字コードで、ワイド文字の 文字列の終りを表す。可搬性のある文字セット (著者注：つまり ASCII) を表現 するために使用されるワイド文字の値は、1 文字で文字を表している時にはその (ASCII での)値に等しくなる。その他の文字のワイド文字コードはロケールおよ び実装に依存する。状態シフトバイトにはワイド文字コード表現はない。

結論としては、可搬性のあるプログラムでは非 ASCII 文字を文字列のリテラル として使うべきではないということです。つまり Unicode の二重引用符がコー ド U+201C および U+201D ということを知っていたとしても、C のプログラムでは 文字列のリテラル `L"\u201cHello\u201d, he said"` や `"\xe2\x80\x9cHello\xe2\x80\x9d, he said"` を使用すべきではありません。そのかわりに GNU gettext を使用し て `gettext("'Hello', he said")` と書き、メッセージデータベース en.UTF-8.po を作成して "'Hello', he said" を "\u201cHello\u201d, he said" に翻訳します。

以下は各 UNIX における ISO/ANSI C 機能の可搬性を調査したものです。GNU glibc-2.2 はこれら全てをサポートする予定ですが、現在の所は(まだ出ていな いため)以下のもので我慢するしかありません。

GNU glibc-2.0.x, glibc-2.1.x

  - <wchar.h> および <wctype.h> があります
  - wcs/mbs 関数がありますが、fgetwc/fputwc/wprintf はありません
  - UTF-8 ロケールはありません
  - mbrtowc は文字コードが 0x80 以上の場合、EILSEQ を返します
Solaris 2.7

  - <wchar.h> および <wctype.h> があります
  - wcs/mbs 関数、fgetwc/fputwc/wprintf 関数、全てあります。
  - 次の UTF-8 ロケールがあります： en_US.UTF-8, de.UTF-8, es.UTF-8, fr.UTF-8, it.UTF-8, sv.UTF-8.
  - mbrtowc は文字コードが 0x80 以上の場合、(-2 ではなく) -1/EILSEQ を返します
OSF/1 4.0d

  - <wchar.h> および <wctype.h> があります
  - wcs/mbs 関数、fgetwc/fputwc/wprintf 関数、全てあります
  - アドオンの universal.utf8@ucs4 ロケールがあります。"man 5 unicode" を参照してください
  - mbrtowc は UTF-8 を扱えません
Irix 6.5

  - <wchar.h> および <wctype.h> があります
  - wcs/mbs 関数、fgetwc/fputwc 関数はありますが、wprintf はありません
  - マルチバイトのロケールはありません
  - mbstate_t のダミーな定義しかありません
  - mbrtowc がありません
HP-UX 11.00

  - <wchar.h> はありますが、<wctype.h> はありません
  - wcs/mbs 関数、fgetwc/fputwc 関数はありますが、wprintf はありません
  - C.utf8 ロケールがあります
  - mbstate_t がありません
  - mbrtowc がありません
AIX 4.2

  - <wchar.h> はありますが、<wctype.h> はありません。 代わりとして <ctype.h> と <wchar.h> を使います
  - wcs/mbs 関数、fgetwc/fputwc 関数はありますが、wprintf はありません
  - 次の UTF-8 ロケールがあります： ET_EE.UTF-8, LT_LT.UTF-8, LV_LV.UTF-8, ZH_CN.UTF-8.
  - mbstate_t がありません
  - mbrtowc がありません

以上より、再開可能(restartable)かつマルチスレッドセーフな wcsr/mbsr 関数 を使うことをお勧めします。関数が無いシステム(Irix, HP-UX, AIX)のことは忘れ てしまって、UTF-8 ロケール のプラグイン libutf8_plug.so(下記参照のこと)を、 wcsr/mbsr 関数を使用したプログラムをコンパイルできるシステム(Linux, Solaris, OSF/1)で使用しましょう。

Sun の同様なアドバイス http://www.sun.com/software/white-papers/wp-unicode/ の "Internationalized Applications with Unicode" セクションにはこのように書 かれています：

> 正しくアプリケーションを国際化するには、次のガイドラインに沿う必要がある：

1. Unicode への直接のアクセスは避ける。 これはプラットフォーム上の国際化フレームワークの仕事です
2. POSIX モデルの、マルチバイトおよび ワイド文字インターフェースを使用する
3. 国際化フレームワークの提供する言語および文化依存操作の API のみを呼びだす
4. コードセット非依存のままにする

もし何かの理由で、`wchar_t' が Unicode だとどうしても仮定しなければなら ない(例えば一部の Unicode 文字に特別な処理を行ないたい時)ようなコードが 必要な時には、コードのその部分を `is_locale_utf8()` の結果を見て 動作するようにするべきです。そうしない場合そのプログラムは、ロケールや他 のプラットフォームでの動作の記述でぐちゃぐちゃになってしまうでしょう。 `is_locale_utf8` 関数は utf8locale.h で宣言され、 utf8locale.c で定義されています。

### libutf8 ライブラリ

ISO/ANSI C API の可搬性のある実装で、8 ビットのロケールと UTF-8 ロケール をサポートします。これは libutf8-0.6.2.tar.gz より取得できます。

利点：

- Unicode UTF-8 を可搬性のある形でサポートします。マルチバイト文字やワイド 文字をまったくサポートをしていない、もしくは正しく動作しない OS であって もです。
- 8 ビットロケールおよび UTF-8 ロケールをサポートする全ての OS で 同一のバイナリを実行することができます。
- OS ベンダーが適切なマルチバイト文字サポートを追加すれば、 それを -DHAVE_LIBUTF8 コンパイルオプションなしで単純に再コンパイルするだ けで利用することができます。

### Plan9 の場合

Unix の一種である Plan9 オペレーティングシステムは、全てのアプリケーションの 文字エンコーディングとして UTF-8 を使用しています。ワイド文字型は ``wchar_t`' ではなく、``Rune`' です。Rob Pike と Howard Trickey が書いたライブラリは ftp://ftp.cdrom.com/pub/netlib/research/9libs/9libs-1.0.tar.gz にあります。Alistair G. Crooks の書いたその他の同様なライブラリは ftp://ftp.cdrom.com/pub/NetBSD/packages/distfiles/libutf-2.10.tar.gz です。これらのライブラリには UTF-8 を扱える正規表現があります。

この API の欠点：

- UTF-8(サポート)はオプションではなく、コンパイル時に含まれてしまいます。 これによりプログラムは、ヨーロッパでまだ数多く使用されている 8 ビットエ ンコーディングをサポートできません。

### GUI 用には

Qt-2.0 ライブラリ http://www.troll.no/ には、Unicode に完全対応した QString クラス があります。UTF-8 でエンコードされたテキストとの相互変換には、メンバ関数 の QString::utf8 および QString::fromUtf8 を使うことができます。 QString::ascii および QString::latin1 メンバ関数はもはや使うべきではありま せん。

### 先進的なテキスト操作

これまでに述べたライブラリは、ASCII を扱えるものを Unicode を扱えるよう にしたバージョンです。ここで述べるのは titlecase(単語の始まりを大文字に すること、またはその反対)、句読点と記号の区別、標準的な分かち書き (decomposition)、結合を行うクラス、標準的なソートといった機能を Unicode で扱えるライブラリです。

ucdata-2.3

  Mark Leisher の ucdata library http://crl.nmsu.edu/~mleisher/ucdata.html には、文字のプロパティ、 大文字と小文字の変換、分かち書き(decomposition)、結合を行うクラスがあります。

ICU

  IBM の Unicode クラス(IBMs Classes for Unicode) http://www.alphaworks.ibm.com/tech/icu/ です。総合的な国際化ライ ブラリで、Unicode の文字列、リソースバンドル、数値のフォーマッタ、日付／ 時間のフォーマッタ、メッセージフォーマッタ、照合などの機能があります。多 くのロケールがサポートされており、Unix と Win32 の間で可搬性がありますが、 そのままでは Linux の libc6 上でしかコンパイルできません(libc5 では駄目です)。

libunicode

  Tom Tromey 達による、GNOME libunicode library http://cvs.gnome.org/lxr/source/libunicode/ です。これには文字セッ トのコンバート、文字のプロパティ、分かち書き(decomposition)の機能があり ます。

### 変換用には

2 種類の変換ライブラリがあります。これは UTF-8 や、 多くの 8 ビット文字セットをサポートしています。

### iconv

GNU glibc-2.1.1 に含まれている、Ulrich Drepper による iconv の実装 ftp://ftp.gnu.org/pub/gnu/glibc/glibc-2.1.1.tar.gz

Bruno Haible による可搬性のある iconv ftp://ftp.ilog.fr/pub/Users/haible/gnu/libiconv-1.1.tar.gz

Konstantin Chuguev <joy@urc.ac.ru> による可搬性のある iconv ftp://ftp.urc.ac.ru/pub/local/OS/Unix/converters/iconv-0.4.tar.gz

利点：

- iconv は POSIX 標準であり、iconv を使用して UTF-8 との相互変換を行うプロ グラムは Solaris でも動作します。ただし文字セットの名前はプラットフォー ム間で異なっています。例えば glibc では "EUC-JP" ですが、HP-UX では "eucJP" になっています(この文字セットの公式な IANA での名前は "EUC-JP" ですから、明らかに HP-UX が間違っています)。
- glibc-2.1 では他にライブラリを必要としません。その他の環境では、Bruno Haible や Konstantin Chuguev の実装した iconv を使用することができます。

### librecode

François Pinard の librecode ftp://ftp.gnu.org/pub/gnu/recode/recode-3.5.tar.gz

利点：

- 字訳(transliteration)をサポートしています。これは非 ASCII 文字を ASCII 文字へと、欠落のない(lossless)変換が不可能な場合であっても人間が読める形 にコンバートします。

欠点：

- 標準外(non-standard) な API
- 初期化が遅い

### その他のアプローチ

libutf-8

  G. Adam Stanislav <adam@whizkidtech.net>の libutf-8 には UTF-8 エンコードさ れた `FILE*' ストリームとの相互変換を動的に行なういくつかの関数がありま す。 http://www.whizkidtech.net/i18n/libutf-8-1.0.tar.gz

  利点：

  - 非常に小さい

  欠点：

  - 標準外(non-standard) な API
  - UTF-8 はオプションではなくコンパイル時に含まれてしまいます。これにより、 このライブラリとコンパイルされたプログラムは 8 ビットのエンコーディング をサポートしません。こういったプログラムはヨーロッパではまだ数多く使用さ れています。
  - インストールは簡単ではありません。autoconfig されないため、Makefile をい じる必要があります。

## 6.2 Java

Java は言語自体が Unicode をサポートします。`char' 型は Unicode 文字を表 し、また `java.lang.String' クラスは Unicode の文字列を扱えます。

Java はどんな Unicode 文字でも、自身のウィンドウシステム AWT を使用して 表示することができます。このために必要なことは、 1. Java のシステムプロパティ "user.language" を適切なものにセットする。 2. /usr/lib/java/lib/font.properties.language フォントセットの 定義を適切なものにセットする。 3. 上記で指定したフォントをインストールする。例えば日本語の文字 を含むテキストを表示するには、日本語のフォントをインストールし、"java - Duser.language=ja ..." で実行します。フォントセットを組み合わせることも できます：西ヨーロッパ、ギリシャ、日本語の文字を同時に表示するには、 "font.properties" (ISO-8859-1 をカバー)、"font.properties.el" (ISO-8859-7 をカバー)、"font.properties.ja" ファイルを組み合わせたものを 作成します。??This is untested??

java.io.DataInput および java.io.DataOutput インターフェースにはそれぞれ、 `readUTF' や `writeUTF' というメソッドがありますが、これらは UTF-8 を使 用しないことに注意してください。これらは変更された UTF-8 を使用します： NUL 文字は 0x00 のかわりに 2 バイトの 0xC0 0x80 としてエンコードされ、末 尾に 0x00 バイトが追加されます。このようにエンコードされるため、文字列は フィールドの長さを持たなくとも、NUL 文字を含むことができます。C では <string.h> の strlen() や strcpy() といった関数でそのような NUL 文 字を含んだ文字列を扱うことができます。

## 6.3 Lisp

Common Lisp 標準では `base-char' と `character' の 2 種類の文字列型を定 義しています。Unicode をサポートするかどうかは実装に依存します。文字セッ トやエンコーディングを指定するためには、引数として `:external-format' を 指定し、`open' で定義します。

フリーの(訳注：無料ではなく、自由という意味です) Common Lisp の実装では、 CLISP http://clisp.cons.org/ だけが Unicode をサポートしていますが、そ のためには CLISP の 1999年 7月以降のバージョン ftp://clisp.cons.org/pub/lisp/clisp/source/clispsrc.tar.gz が必 要となります。`base-char'型 および `character' 型はどちらも 16-bit Unicode と同等です。ファイル、ソケット、パイプの I/O に使用されるエンコーディン グは `:external-format' 引数で指定することができます。ファイル、ソケット、 パイプの I/O のデフォルトのエンコーディングおよび tty I/O に使用されたエン コーディングはロケールに依存しています。

商用の Common Lisp の実装では、Eclipse http://www.elwood.com/eclipse/eclipse.htm だけが Unicode をサポー トします。 http://www.elwood.com/eclipse/char.htm を参照してください。 `base-char' 型は ISO-8859-1 と同等であり、また `character' 型は全ての Unicode 文字を含むことができます。ファイルの I/O に使用されるエンコーディ ングの指定は `:element-type' および `:external-format' 引数で `open' を指 定することにより可能です。制限事項： 文字属性(character attribute attribute) 関数はロケールに依存しています。ソースおよびコンパイルされたソー スファイルは Unicode の文字列でのリテラルを扱うことができません。

商用の Common Lisp の実装である Allegro CL はまだ Unicode をサポートしま せんが、Erik Naggum が現在作業中です。

## 6.4 Ada95

Ada95 は Unicode をサポートするためにデザインされました。Ada95 の標準ラ イブラリの機能には、ISO 10646-1 のための特別なデータ型である Wide_Character および Wide_String があります。これらに関連する多数のプロシー ジャや関数も同様です。GNU Ada95 コンパイラ(gnat-3.11 以降)は UTF-8 をワ イド文字の外部エンコーディングとしてサポートします。これにより、UTF-8 を ソースコードとアプリケーションの I/O として使用することができます。アプ リケーションでも使用できるようにするにはファイルをオープンする時に FORM 文字列 に "WCEM=8" をセットし、またソースコードが UTF-8 ならコンパ イラオプション "-gnatW8" を使用します。詳しくは GNAT および Ada95 のリファ レンスマニュアルを参照してください。

次のページ 前のページ 目次へ