For Development HEAD DRAFTSearch (procedure/syntax/module):

9.5 gauche.charconv - 文字コード変換

Module: gauche.charconv

与えられたデータストリームの文字エンコーディングを変換するための手続き群を提供する モジュールです。

このモジュールはまた、open-input-filecall-with-output-file等のファイルストリームを作成する手続きに :encodingキーワード引数が与えられた場合に暗黙にロードされます。

ポータブルなコードには、SRFI-181で定義される符号変換ポートを使うこともできます (符号変換ポート参照)。


9.5.1 サポートされる文字エンコーディング

CESの名前は文字列またはシンボルで指定します。大文字小文字の違いは無視されます。 同じCESにいくつかの別名がついていることがあります。

CES名 "none" は特殊で、文字列を単なるバイト列として扱い、そのエンコーディングの解釈は アプリケーションに任されます。したがって、CES "none" への変換、および "none" からの変換は「何もしない」変換として扱われます。

Gaucheは自前で、Unicode Transfer Encoding (UTF-8, UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, UTF-32LE)、Latin-N (ISO8859-1から16)、 そして主要な日本語エンコーディング (ISO2022JP, ISO2022JP-3, EUC-JP (EUC-JISX0213), Shift_JIS (Shift_JISX0213))、 の間の変換を行います。

上記以外のコードとの変換はデフォルトでiconv(3)を利用します。 ただ、iconv(3)のAPIには、出力CESでエンコードできない文字が入力に 含まれていた場合の振る舞いをカスタマイズするインタフェースがありません。 そこまでの制御が必要なら、次のパラメータでiconv(3)の使用を止めることができます。

Parameter: external-conversion-library

このパラメータの値はシンボルiconv#fでなければなりません。 デフォルトの値はiconvです。

このパラメータの値がiconvである間にオープンされた変換ポートは、 要求された変換がGauche内部でサポートされていなかった場合にiconv(3)ライブラリを 使います。変換ポートがopenされる時点でのパラメータの値が重要です–一旦オープンされたら、 パラメータの値はそのポートの動作に影響を与えません。

特定の変換がシステムでサポートされているかどうかは次の手続きで調べることができます。

Function: ces-conversion-supported? from-ces to-ces

{gauche.charconv} CES from-cesからto-cesへの変換がサポートされていれば#tを、 そうでなければ#fを返します。

システムがfrom-cesからto-cesへの変換を部分的にしか サポートしていない場合でもこの手続きは#tを返すことに注意して下さい。 そのような場合、実際の変換作業はfrom-cesにあってto-cesにない コードを置換文字に置き換える等で情報を失うかもしれません (例えば、UnicodeからEUC-JPへの変換はサポートされていますが、 UnicodeにはEUC-JPにない文字も含まれています)。

from-cesto-cesが "none" ならばこの手続きは常に#tを 返します。なぜならそのような変換は常に成功するからです(何も変換しないわけですが)。

この手続きは、パラメータexternal-conversion-libraryに影響を受けます。

また、CESを扱う二つの便利な手続きが用意されています。

Function: ces-equivalent? ces-a ces-b :optional unknown-value

{gauche.charconv} CES ces-aces-bがシステムの知る限りで等価だった場合に #tを、等価でない場合に#fを返します。 システムがその等価性を判断出来ない場合はunknown-valueに与えられた 値を返します。そのデフォルトは#fです。

CES名 "none" はワイルドカードのように動作します。それはどんなCESとも 等価と見倣されます。(従って、ces-equivalent? は遷移的ではありません。 この手続きは、二つのCESを知った時に相互の変換が 必要かどうかを判断するためのものです)。

(ces-equivalent? 'eucjp "EUC-JP")            ⇒ #t
(ces-equivalent? 'shift_jis "EUC-JP")        ⇒ #f
(ces-equivalent? "NoSuchEncoding" 'utf-8 '?) ⇒ ?
Function: ces-upper-compatible? ces-a ces-b :optional unknown-value

{gauche.charconv} CES ces-bでエンコードされた文字列が、システムの知る限りで 変換無しにces-aでエンコードされたものと見倣せる場合に#tを 返します。見倣せない場合は#fを返します。 システムが判断できない場合はunknown-valueに与えられた 値を返します。そのデフォルトは#fです。

ces-equivalent?と同様に、CES名 "none" はワイルドカードとして 働きます。ces-aces-b が "none" であれば、 常に#tが返されます。

(ces-upper-compatible? "eucjp" "ASCII")             ⇒ #t
(ces-upper-compatible? "eucjp" "utf-8")             ⇒ #f
(ces-upper-compatible? "utf-8" "NoSuchEncoding" '?) ⇒ ?

出力CESへマップ不可能な入力文字に出会った場合のGaucheの内部変換ルーチンの動作は、 illegal-outputキーワード引数で指定される 変換ポートの不正出力ハンドリングモードで決まります。 モードがraiseなら<io-encoding-error>が投げられます。 モードがreplaceなら、その文字は置換文字に置き換えられます。

変換先の文字エンコーディングで UniocdeのU+FFFD (REPLACEMENT CHARACTER) が使えればそれが置換文字となります。日本語エンコーディングではその文字は 使えませんが、伝統的にgeta mark ’〓’ (U+3013) が使われてきたので それに準じてU+3013を使います。どちらの文字も使えなければ?に置換します。

入力CESでシングルバイトである場合は ’?’ に置換します。 iconvによる変換でマップ不可能な文字に出会った場合の処理はiconvの実装に 依存します(例えばglibcではエラーとなります)。

変換ルーチンが、入力CESとして不正な入力文字列に出会った場合は <io-decoding-error>が報告されます。

UTFエンコーディングとBOM

Unicode文字U+FEFF (Zero-Width No-Break Space) はUTFストリームの最初に 現れると特別な意味を持ちます。後続のUTFデータのバイトオーダーを記述する BOM (Byte-order mark)になるのです。 UTF-16とUTF-32ではバイトオーダーを知ることは決定的に重要です。 一方、UTF-8ではバイトオーダーは関係ないのでBOMは必要ではありませんが、 いくつかのソフトウェアはデータがUTF-8であることを判別するためだけに BOMを付加することがあります。

技術的な観点からは、BOMはテキストの内容の一部ではなく、フォーマットに関するメタ情報です。 これはちょっと困った事態を引き起こします。データストリームを扱う時に、 テキストの内容を読みたい場合と、メタ情報を含めた全体を扱いたい場合があるからです。 伝統的に、この二つは厳密には区別されず、アドホックな方法で処理されてきました。 Gaucheでは指定されたエンコーディングに応じて以下のとおりにしています。

UTF-8

BOMは特別扱いされません。入力の最初にU+FEFFがあれば、それは文字#\ufeff として読まれます。出力の場合、BOMは付加されません。 これがI/Oでのデフォルトの挙動です。

UTF-8-BOM

これは、UTF-8だけれども入力データにBOMがあればそれをただ無視するという 「擬似」エンコーディングです。UTF-8ストリームの先頭のBOMの有無を気にしたくない プログラムのために用意されています。 このエンコーディングは出力には使えません。UTF-8の出力にどうしてもBOMをつける必要があるなら、 最初に#\ufeffを書き出してください。

UTF-16, UTF-32

入力はBOMを認識してバイトオーダーを決定します。BOM自身は読まれるデータには含まれません。 BOMが無かった場合はビッグエンディアン(UTF-16BE, UTF-32BE)が使われます。 出力の場合はBOMが先頭に付加されます。

UTF-16LE, UTF-32LE, UTF-16BE, UTF-32BE

これらのエンコーディングでは、メタ情報は既に別の方法で与えられていて 呼び出し元は入力データのバイトオーダーを既に知っているものとみなします。 従ってBOMは特別扱いされません。入力の最初にU+FEFFがあれば、それは文字#\ufeff として読まれます。出力の場合、BOMは付加されません。

Gaucheの内部変換アルゴリズムの詳細

EUC_JP、Shift JIS、及びISO2022JP間の変換は可能な限り計算で行います。 文字が未定義のコードポイントも計算式に従って変換されます。 Unicode(UTF-8)とEUC_JP間の変換はテーブルルックアップによって行われます。 UnicodeとShift JISまたはISO2022JP間の変換は、入力CESを一度EUC_JPに 変換し、それを出力CESに変換することによって行います。 ISO8859-NはUnicodeにへテーブルで変換され、必要ならばそこから出力CESに変換されます。 入力と出力のCESがGaucheの内部アルゴリズムがサポートする範囲で等しかった場合、 Gaucheの変換ルーチンはエンコーディングの正当性はチェックせず、 単に入力を出力にコピーします。

EUC_JP, EUCJP, EUCJ, EUC_JISX0213

ASCII, JIS X 0201カナ、JIS X 0212、及びJIS X 0213文字集合をカバーします。 JIS X 0212は、単にJIS X 0213と重ならないコードを使っているためにサポート されていますが、他のCESへの変換は行われないので注意して下さい。 なるべくJIS X 0213の使用をおすすめします。

SHIFT_JIS, SHIFTJIS, SJIS

Shift_JISX0213をカバーします。但し、0x5cと0x7eに関しては JIS X 0201 Roman (YEN SIGNとOVERLINE) ではなく ASCII (REVERSE SOLIDUSとTILDE) にマップされます。

UTF-8, UTF8

Unicodeです。JIS X 0213中の文字のいくつかはExtension B (U+20000〜) にマップされます。JIS X 0213中の他の文字のいくつかは2つのUnicode文字 (base character + combining character)にマップされます。

ISO2022JP, CSISO2022JP, ISO2022JP-1, ISO2022JP-2, ISO2022JP-3

これらのエンコーディングは、CSISO2022JPがISO2022JPのエイリアスであることを除き 異なるエスケープシーケンスを使いますが、Gaucheの中では同じルーチンで処理されます。 これらのエンコーディングのいずれかが入力CESに指定された場合、 Gaucheは全てのバリエーションのエスケープシーケンスを認識します。 但し、ISO2022JP-2に定義された日本語以外の言語のエスケープシーケンスに関しては、 Gaucheはエスケープシーケンスの認識だけを行い、 文字は’?’あるいは’〓’ に置換します。

出力に関してはGaucheは出来る限りISO2022JPでエンコードし、 JIS X 0212文字に出会ったらISO2022JP-1のエスケープシーケンスを、 JIS X 0213の第2面の文字に出会ったらISO2022JP-3のエスケープシーケンスを 用います。したがって、文字列がJIS X 0208の範囲だけで構成されていた場合は 出力はISO2022JP互換となります。 厳密には、JIS X 0213では包摂基準の変更により、 「JIS X 0208と同じコードポイントを用いるがJIS X 0208のエスケープシーケンスを 使ってはいけない文字」というのが定義されています。 Gaucheでは互換性のため、これらの文字もJIS X 0208のエスケープシーケンスを 用いてエンコードします (これは、Emacs-Muleにおけるiso2022jp-3-compatible モードと同じ方針です)。


9.5.2 文字エンコーディングの自動判定

しばしば、入力のCESが未知であり、可能性のあるいくつかの候補から入力のCESを 推測しなければならない場合があります。推測するアルゴリズムはいくつも考えられるので、 それぞれに名前がついています(ワイルドカードCES)。 今のところ、一つのアルゴリズムしか実装されていません。

"*JP"

日本語のテキストのCESを、ISO2022-JP(-1,2,3), EUCJP, SHIFT_JIS または UTF-8の いずれかであるとして推測する。

(入力がBOMつきUTF-8であった場合も、UTF-8-BOMではなくUTF-8が認識結果となります)。

ワイルドカードCESは、いくつかの変換関数においてCES名の代わりに使うことができます。

Function: ces-guess-from-string string scheme

{gauche.charconv} 文字列stringのCESを、CES推測アルゴリズムscheme ("*JP"など) を使って推測し、結果のCES名を返します。もしどのCESにも該当しないデータが 含まれていれば#fが返されます。複数のCESが可能である場合、 ネイティブエンコーディングが含まれていればそれを、 そうでなければ可能なCESからどれかひとつが選んで返されます。


9.5.3 変換ポート

Function: open-input-conversion-port source from-code :key to-code buffer-size owner? illegal-output

{gauche.charconv} 文字が符号化方法from-codeで符号化されているデータを読み出せる 入力ポートsourceを取り、符号化方法to-codeで符号化されたデータを 読み出せるポートを作成して返します。

to-codeが省略された場合はネイティブのCESと見なされます。

buffer-sizeは変換のための内部バッファのサイズを指定します。 省略時のサイズは1Kバイト程で、通常の使用には問題ないサイズです。

illegal-output引数は、出力CESに入力に対応する文字がない場合の動作を指定します。 引数はシンボルraisereplaceでなければなりません。 raiseの場合は、<io-encoding-error>が投げられます。 replaceの場合は、出力CESに応じた適切な置換文字が代わりに出力されます。 省略された場合はraiseの動作となります。

iconv(3)ライブラリは、ハンドリングモードを指定するAPIを持っていないことに 注意してください。変換がiconv(3)に任されると、illegal-outputの指定は 無視され、動作はiconv(3)の実装に依存します。 illegal-outputの指定通りに動いて欲しい場合は、パラメータ external-conversion-library#fに束縛している環境で この手続きを呼んでください。そうすると、変換ポートはiconv(3)を使わず、 Gauche内部で変換できない場合はエラーを投げます。

通常、open-input-conversion-portは変換が全て終了した後でもsource はオープンしたままにします。キーワード引数owner?に真の値を指定すると、 EOFが読まれた後でsourceはクローズされます。

入力のCESが不明な場合、"*JP"などのCES推測アルゴリズム名をfrom-code として指定することができます。変換ポートは最高でバッファサイズまでのデータを先読みし、 CESを推測しようとします。そのアルゴリズムで推測されるどのCESにも該当しないデータが 検出された場合はエラーが報告されます。もし複数のCESが可能であるようなデータであった 場合は、Gaucheのネイティブエンコーディングが可能なCESに含まれていればそれが選ばれ、 そうでなければいずれかのCESが適当に選ばれます。従って、バッファサイズが小さすぎると 誤った判定をする可能性が高くなります。大抵のテキストドキュメントに対して、 既定のバッファサイズは十分ですが、大きなテキストのほとんどがASCII文字で最後だけ マルチバイト文字が現われるようなテキストでは誤判定の可能性があります。 最悪の場合でも結果を保証するには、テキスト全体が格納できるバッファサイズを指定すると 良いでしょう。

例を示しましょう。以下のコードは未知のCES(但しEUC-JP, SJIS, ISO2022-JP, UTF8の いずれかであることは分かっている)で書かれたテキストファイルunknown.txtを 読みだし、文字エンコーディングをEUC-JPに変換してeucjp.txtに書き出します。

(call-with-output-file "eucjp.txt"
  (lambda (out)
    (copy-port (open-input-conversion-port
                 (open-input-file "unknown.txt")
                 "*jp"             ;guess code
                 :to-code "eucjp"
                 :owner? #t)       ;close unknown.txt afterwards
               out)))

ポータブルなコードでは、SRFI-181のtranscoded-portを使うこともできます (符号変換ポート参照)。

Function: open-output-conversion-port sink to-code :key from-code buffer-size owner? illegal-output

{gauche.charconv} 文字のエンコーディングをfrom-codeからto-codeに変換して 出力ポートsinkに書き出すような出力変換ポートを作成して返します。 from-codeが省略された場合はネイティブのCESと見なされます。 from-codeにもto-codeにも、CES推測アルゴリズム名を 使用することはできません。

buffer-sizeは内部で変換に使われるバッファサイズを指定します。 出力変換ポートに書き出された文字は、flushを使って明示的に フラッシュするか出力変換ポートが閉じられるまで、バッファ内に残っている可能性があります。

通常、出力変換ポートがクローズされてもsinkはクローズされません。 owner?に真の値を与えれば、出力変換ポートのクローズ時にsinkを クローズするようにできます。

illegal-outputキーワード引数は、 open-input-conversion-portと同じです。

ポータブルなコードでは、SRFI-181のtranscoded-portを使うこともできます (符号変換ポート参照)。

Function: ces-convert-to return-type source from-code :optional to-code :key illegal-output
Function: ces-convert source from-code :optional to-code :key illegal-output

{gauche.charconv} 文字エンコーディングfrom-codeでエンコードされた 文字列もしくはu8vectorのsourceを変換し、 文字エンコーディングto-codeでエンコードされた文字列またはu8vectorを返します。 to-codeが省略された場合はネイティブエンコーディングへと変換されます。

ces-convert-toでは、戻り値の型をreturn-typeで指定します。 return-typeはクラスオブジェクト<string><u8vector> でなければなりません。一方、ces-convertは、sourceの型にかかわらず 常に文字列を返します。

ネイティブエンコーディング以外のエンコーディングをto-codeに指定して 文字列が返された場合、それは不完全な文字列であるかもしれません。 これは後方互換性のためです。一般的には、ネイティブエンコーディング以外の マルチバイトシーケンスを扱う場合はu8vectorを使うことをおすすめします。

from-codeにはCES推測アルゴリズム名("*JP"など)を与えることができます。

キーワード引数illegal-outputは出力側でエンコードできない文字が 入力された場合の振る舞いを指定します。上のopen-input-conversion-portの 説明を参照してください。デフォルトでは<io-encoding-error>が投げられます (ただし、変換がiconv(3)に任された場合、 その振る舞いは外部ライブラリの実装に依存します)。

ポータブルなコードでは、SRFI-181のbytevector->string およびstring->bytevectorを使うこともできます (符号変換ポート参照)。

Function: call-with-input-conversion iport proc :key encoding conversion-buffer-size illegal-output
Function: call-with-output-conversion oport proc :key encoding conversion-buffer-size illegal-output

{gauche.charconv} この2つの手続きを使うと、元のポートのエンコーディングとは異る一時的な エンコーディングをもつ文字I/Oを行うことが可能です。

call-with-input-conversion は、文字エンコードencoding を もつ入力ポート iport をとり、procを一つの引数、すなわち、 変換入力ポートとともに呼びだします。このポートから、proc は文字を Gauche の内部エンコーディングで読み込むことができます。いったん、 procが呼ばれたら、EOFまでのすべての文字を読み込まなくてはなりません。 後述の注意を参照してください。

call-with-output-conversion は、文字エンコーディング encoding を期待する出力ポート oport をとり、procを一つの引数、すなわち、 一時的な変換出力ポートとともに呼びだします。このポートへ、procは Gauche の内部エンコーディングをもつ文字を書き込むことができます。 proc が戻るか、エラーで抜けると、この一時的な変換出力ポートは フラッシュされて、クローズされます。call-with-output-conversion の呼出し側は、その後、元のエンコーディングのポートoportを使い 続けること可能です。

どちらの手続きも proc が返す値を返します。encoding のデフォルト値は Gauche の内部エンコーディングです。この2つの手続きは 必要がなければ、変換ポートを作成しません。もし conversion-buffer-size が与えられていれば、変換ポートがオープン されるときに、buffer-size 引数として使われます。

proc がアクティブであるあいだは、iport/oport を直接 使うべきではありません。文字のエンコーディングはステートフルなプロセスで 変換ポート(から/へ)の入出力を混在させると元にあるポートの状態がおかしく なってしまいます。

注意: call-with-input-conversion については、 proc が EOF を読むまでは、iport を再度利用することはできません。 これは、変換ポートがバッファを必要とし、proc がもどったときに iportへのバッファ付き入力をアンドゥする方法がないからです。

Function: with-input-conversion iport thunk :key encoding conversion-buffer-size illegal-output
Function: with-output-conversion oport thunk :key encoding conversion-buffer-size illegal-output

{gauche.charconv} call-with-*-conversion と似ていますが、この2つの手続きは、 thunk を引数なしで呼びます、また、変換ポートはそれぞれ標準入力、標準 出力のポートにセットされます。 キーワード引数の意味は call-with-*-conversion と同じです。

Function: wrap-with-input-conversion port from-code :key to-code owner? buffer-size illegal-output
Function: wrap-with-output-conversion port to-code :key from-code owner? buffer-size illegal-output

{gauche.charconv} 不要な変換ポートを作らないようにする便利な手続きです。 各手続きはそれぞれopen-input-conversion-portopen-output-conversion-portのように動作します。 但し、指定されたCES間の変換が必要でないとシステムが知っている場合は、 変換ポートは作られず、portがそのまま返されます。

変換ポートが作られた場合、portは常に変換ポートに所有されます (:owner #tとなる)。ポートを閉じる場合はportではなく 常にwrap-with-*-conversionが返したポートを閉じるようにして下さい。 元のportが先に閉じられた場合、変換中の結果が書き出されない可能性があります。 (変換によっては、ポートが閉じられる時点で終了シーケンスを書き出すものがあるので、 単にポートをflushするだけでは不十分です)。

buffer-sizeillegal-output引数はそのまま 変換ポート作成手続きに渡されます。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT