Calendar

July 2010
M T W T F S S
« Jun   Aug »
 1234
567891011
12131415161718
19202122232425
262728293031  

STLのロケールについて


はじめに

Javaにロケール機能があるように、C++標準ライブラリ(STL)もロケールをサポートしています。 STLのロケールはあまり注目されることはありませんでしたが、ある時、 (一部の)Windows系プログラマの注目を浴びることになりました。

それは…
Visual C++ 2005 では、fstream系に日本語のパスを与えると実行時エラーになる!
という問題が起きたからです。

例えば、VC++6では問題なく動いていた次のようなコードが、VC++2005でビルドすると実行時にファイルオープンエラーになりました。

  ofstream ofs(日本語のパス);

この時の回避策が「ロケールを日本語に設定する」というものでした。
こんな感じで

#include <locale> 

  locale::global(locale("japanese"));    // 先に設定しておく
  ...
  ofstream ofs(日本語のパス);

当時は上のような対応をした人が多かったと思います。(さらっと1行書いて、無事解決♪)

しかし、この対応には、ちょっと(?)問題がありました。

ロケールとは?

プログラミング言語C++ 第3版 21.7節 ロケール (P740) によると

localeは、文字を記号、数字以外の文字、数字等々に分類する方法、文字列の照合順、入出力時の数値の形式などを制御するオブジェクトである。一般に、iostreamライブラリは、なんらかの言語、文化の習慣を活かすために、暗黙のうちにlocaleを使う。

とあります。 つまり、(当たり前ですが)日本語のパスを扱うためでなく、もっと別の(高尚な)目的のために用意されているのです。

“入出力時の数値の形式などを制御する”の意味は、数値を3桁毎のカンマ(,)区切りに整形したり、各国の文化に適した書式に整形することを意味します。

例えば…

1234567  ===> 1,234,567

などです。 Javaのロケールも同様ですよね?

何が問題か?

では、 locale::global(locale(“japanese”)); の何が問題でしょうか?

例えば次のコード

  // デフォルトのロケール
  ofstream ofs(ファイル名);
  ofs << 1234567L << endl;

出力結果は、

1234567

となります。

しかし次のコードでは、

  locale::global(locale("japanese"));  // ロケール指定

  ofstream ofs(日本語のパス);
  ofs << 1234567L << endl;

出力結果は、

1,234,567

となってしまいます。

好ましい対応策

手っ取り早いのは、VC++のバージョンを上げてしまうことです。ちなみに、VC++2010(Express Edition)では、locale::global() を使わなくても日本語のパスを扱えました。

それが無理な場合...

locale::global() で大域localeを変更するのではなく、必要な時にストリーム単位でlocaleを指定します。

  locale default_locale;   // 現在のロケールを保存

  ofstream ofs;
  ofs.imbue(std::locale("japanese"));
  ofs.open(日本語のパス);
  ofs.imbue(default_locale);   // オープンしたらロケールを元に戻す。

  ofs << 1234567L << std::endl;

これで日本語のパスが扱えて、出力結果も、

1234567

となります。

ロケールを使ってみる

せっかくなのでロケールの使い道を考えてみましょう。

例えば、次のように使えば、数値をカンマ(,)区切りの文字列に変換することが出来ます。

  stringstream ss;
  ss.imbue(std::locale("japanese"));
  ss << 1234567L << std::endl;

ss.str()で取り出せば、"1,234,567" という文字列が得られます。

プログラミング言語C++ 第3版 21.7節 ロケール (P741-742) には

locale を明示的に使えば、通貨記号、日付などの入力形式を制御したり、コードセット変換を実行したりすることができる。

と書いてあり、期待を持たせてくれますが、その直後に

しかし、それらの議論は、本書では取り上げない。処理系のドキュメントを参照していただきたい。

とあります (がっくり)

最後に

当時は、Windows系のアプリ開発でWin32APIでなく、わざわざSTLでファイル操作しようという人は少数派だったのかもしれません。 そして現在は、Windows系でC++を使う事自体が少数派かもしれませんが...

当然ですが、g++ でもロケールは使えます。 std::locale("japanese") の代わりに、"ja_JP.UTF-8" になったりと若干注意が必要ではありますが、知っていれば何かの役に立つ事があるかもしれません。

(参考資料)
プログラミング言語 C++ 第3版

Comments are closed.