Opened 8 years ago

Closed 7 years ago

#4 closed task (fixed)

テキストファイルから読み込んだ文字列を wchar_t 配列と Unicode で扱う方法を調査する。

Reported by: murachi Owned by: murachi
Priority: major Milestone: SMFコンパイラ開発
Component: Core Data Version: beta
Severity: MUST Keywords: Unicode, I/O, string
Cc:

Description

具体的な調査内容は以下の通り。

  • iconv について調べる。
    • ライブラリとしての iconv は何をしてくれるのか?
  • iostream について調べる。
    • 通常のテキストファイルを wifstream で読み込んだ場合の動作について。

otoco ではテキストの内部表現はすべて wchar_t 型の Unicode で扱う。

iconv が Unicode 値を扱うものではなかった場合、 Unicode 値を扱える別のライブラリとの併用を考える必要もある。

Change History (22)

comment:1 Changed 8 years ago by murachi

  • Status changed from new to accepted

comment:2 Changed 8 years ago by murachi

使い方としては、 iconv_open() で変換コンテキストハンドル? を生成、そのハンドルを用いて iconv() にて変換を行う、って感じなのかな。基本的には encode to encode (あるいは file format to file format) であって、 wchar_t による Unicode を内部データとして扱うことは想定されていないっぽい。が、 wchar_t と char の相互変換は用意されているようなので、 char でマルチバイトとして読み込み、 from エンコードを適切に指定して wchar_t へ変換、それを内部コードとして扱う、ということはできそう。

ちなみに GNU では libiconv は glibc に組み込まれている模様…。

comment:3 Changed 8 years ago by murachi

しまった… GNU/Linux 環境の wchar_t は 32bits なのか。

サロゲートペアを考慮しなければならない Windows (UTF-16LE) とは事情が大きく異なるんだなぁ…。

大前提として wchar_t は却下、 UTF-8 on char に方針転換した方がいいのかなぁ…。

ちょっと MSVC++2008 も確認してみますか。

comment:4 Changed 8 years ago by murachi

やっぱり Windows + MSVC++2008 では wchar_t は short 型でした…。

Windows だけサロゲートペアに気を遣う実装っていうのもなんだしなぁ…。やっぱり UTF-8 かなぁ…。

comment:5 Changed 8 years ago by murachi

Windows では、 wchar_t は UTF-16LE として扱われる。

GNU/Linux では、 wchar_t は UCS4 (UCS-4-INTERNAL) として扱われる。

もちろん後者の方が理想的なんだけれども (まさに内部コードそのものだし)、どちらにせよ iconv では char* として扱われる。呼び出し側はそれを wchar_t* に reinterpret_cast して利用する。

なんかキモイ。

リンク先では環境に応じて UTF-16LE にするか UCS-4-INTERNAL にするかをプログラム側で切り替えているが、 "wchar_t" を指定したときにこの切り替えが iconv 側でやってくれるのであればすっきりする。

でもそれをやるくらいだったら、始めから内部コードは UCS-4-INTERNAL 一本でやった方がいいような気もするなぁ。で、 wchar_t ではなく、 32bits 固定の別の型を作ってそれで統一するとか…。

comment:6 Changed 8 years ago by murachi

iconv はシンプルでよさげなんだけど、自動認識機能が基本的には備わっていない模様。

ただ、 iconv() は不可逆な変換が行われた文字数を返すようなので、複数候補を試すことによってプログラム側である程度認識が可能かも知れない。つまり、文字セットの優先順位リストを作っておき、文字セットがあらかじめなんなのかが識別できないファイルを読み込んだ場合、その優先順位で全体の iconv() を繰り返し、戻り値が 0 になるものが見つかればその文字セットに決定、無ければ比較した中で最も戻り値が小さいものをファイルの文字セットであると「推定」する。

サンプルプログラムとして、任意のファイルを読み込み、 UTF-8 で出力するプログラムを作成する。但し、読点「、」を「, 」に、区点「。」を「. 」に置き換える。置き換え処理は内部コードを UTF-8 on char* として行うバージョンと、 UCS-4-INTERNAL on ucs4_t* (int32 で typedef する) として行うバージョンの 2通り作成する。

  • UTF-8 on char* と UCS-4-INTERNAL on ucs4_t* とではどちらが実装が容易か。また、その他のメリット・デメリットはあるか。
  • 自動認識処理は推測通り動作するか?

comment:7 Changed 8 years ago by murachi

ていうか、 ucs4_t なんて作らなくても、 uint32_t ってのを使えばいいのかな。でも Windows でも使えるのかなぁ?

comment:8 Changed 8 years ago by murachi

Windows の場合、 uint32_t ってのは無いので、代わりに unsigned int32 を用いる。となるとやっぱり ucs4_t とかに typedef した方がよさげ。まぁ、もしかしたら Windows でも libiconv のヘッダーの中で定義されちゃってるかも知れないけど…。

comment:9 Changed 8 years ago by murachi

下線になってしまった (^_^;。。。正しくは unsigned __int32 ですね。

comment:10 Changed 8 years ago by murachi

上記は本家で、最新のバージョンは 1.13.1 となっている。

が、これらはどうやら nmake ではビルドできない模様。いや、できるのかも知れないが、少なくともパッケージに含まれる README.woe32 ファイルには、

Building requires the mingw or cygwin development environment (includes gcc).
MS Visual C/C++ with "nmake" is no longer supported.

との記述があった。

Windows で利用できる libiconv を配布しているのは以下のいずれか。

前者は 1.9.2 で、元のソースコードからの修正はない模様。

後者は 1.10.20060516 となっているが、これは nightly build ってことになるのかな? MS-UNICODE パッチが当ててある模様、ってことはつまり他のプラットフォームで利用する場合とは動作が異なる可能性があるって事?

…まぁ、 mingw を使うって手もあるっちゃあるんだけどね…。

comment:11 Changed 8 years ago by murachi

とりあえず 1.9.2 の nmake を試みてみたんだが、激しくこけた。サポートしているのは VC++ 7.0 までらしい…。

ビルド済みのライブラリでいけるかなぁ…。

comment:12 Changed 8 years ago by murachi

iconv() 関数の SunOS 向けリファレンスマニュアル。日本語。しかもサンプルプログラム付き。

サンプルプログラムにはエラー処理もしっかり書かれてる。これは非常に参考になる。

JM にも翻訳されたマニュアルがありました。こちらは本家マニュアルの内容に忠実な翻訳。

comment:13 Changed 8 years ago by murachi

コンパイルコマンドは以下の通り。 -l オプションはソースファイル名の後。

gcc -o outfile infile.c -llibiconv

comment:14 Changed 8 years ago by murachi

comment:6 の ucs4_t 版がとりあえず手元で完成。もう少しコードを整理したら commit する。

で、 iconv だが、変換元の文字コードセットに "JAVA" を指定すると、 Shift_JIS のバイナリをエラー無しに変換実行し、文字化けだらけの文字列を生成してしまうことが判明。 "JAVA" は危険なので使用しない方が良さげ。

comment:15 Changed 8 years ago by murachi

"EUC-JISX0213", "ISO-2022-JP-3", "SHIFT_JISX0213" なんて無いって怒られた (汗。

あと、"UCS-4BE", "UCS-4LE" は何でも読み込んじゃう地獄の文字セットっぽいwこいつはリストの後ろの方に持って行かないと危険…。

思っていた以上に文字セット自動認識はうまくいっているっぽい。ていうか速い。ここまで速く動作するとは思わなかった。もうちょっと日本語中心の大きめのファイルで試してみようかな…。

comment:16 Changed 8 years ago by murachi

日本語文章が書かれた HTML ファイルをコピペしまくって 1MB に膨らませたものでテスト。 Shift_JIS の調査順序を一番最後にして、HTML ファイルも Shift_JIS として行ってみたところ、コマンド実行完了までおおよそ 1秒程度。

ファイルの先頭から 100KB (デフォルト; 設定可能) を用いて自動認識を行う、文字セットの調査順序を設定可能とする、といった設計で対応できそう。

comment:17 Changed 8 years ago by murachi

[2] ... とりあえず ucs4_t 版を commit してみた。コメントの充実、マジックバリューの定数シンボル化、シンボル名の整理、その他リファクタリングは後ほど。

comment:18 follow-up: Changed 8 years ago by murachi

*JISX0213 シリーズは無かったが、 -2004 シリーズってのはあるなぁ。これは試しておいた方がいいのかな?

comment:19 in reply to: ↑ 18 Changed 8 years ago by murachi

murachi への返信

*JISX0213 シリーズは無かったが、 -2004 シリーズってのはあるなぁ。これは試しておいた方がいいのかな?

むしろ Vista 以降ではデフォルトにすべきだ罠。

comment:20 Changed 8 years ago by murachi

JIS X 0213 固有の文字を含む SHIFT_JIS 形式のテキストを読み込ませた場合、 SHIFT_JIS と SHIFT_JIS-2004 とで動作の違いがあるか調べる必要がありそう。案外、前者でもエラーは出ないが、 iconv() の戻り値、即ち不可逆変換の回数としてカウントされるかも知れない。

comment:21 Changed 7 years ago by murachi

[6] ... コメントを充実させた。

comment:22 Changed 7 years ago by murachi

  • Resolution set to fixed
  • Status changed from accepted to closed
Note: See TracTickets for help on using tickets.