プロが教える店舗&オフィスのセキュリティ対策術

wstring (または wchar_t)の出力がうまくいかず困っています。
基本的な質問になるかと思いますがよろしくお願いします。

#include <string>
#include <wchar.h>
using namespace std;

int main() {
 wstring str = L"test";
 wprintf(L"%s \n", str.c_str());//(1)
 wprintf(L"%s \n", L"test");//(2)
 wprintf(L"test \n");//(3)
 // cout << str <<endl; //(4)
 cout << str.c_str() <<endl;//(5)

}
出力結果
(1) t
(2) t
(3) test
(4) コンパイルエラー
(5)
望むのは当然(3)の出力です。
web上で(1)や(4)のような記述はみかけたのですが、(4)に関してはなぜコンパイルエラーになるかもわからず。。

os linux.
gcc 4.1.1

A 回答 (2件)

> wprintf(L"%s \n", str.c_str());//(1)


> wprintf(L"%s \n", L"test");//(2)
c_strはワイド文字列のデータ本体をwchar_t *で返すのみ。"test\0"を返す訳ではない。
この場合、(1)も(2)も
 wprintf(L"%s \n", (char *)((wchar_t *)"t\0e\0s\0t\0\0\0"));
に同じ。つまり
 wprintf(L"%s \n", "t");
と書いたのと同じである。

書式sにwchar_t型文字列を引数に与える場合は、書式sにl修飾子を付加する事。

(1)、(2)は以下のようにすると正常に動作する筈。
 wprintf(L"%ls \n", str.c_str());//(1)
 wprintf(L"%ls \n", L"test");//(2)

> wprintf(L"test \n");//(3)
については解説不要。

> // cout << str <<endl; //(4)
#include <iostream>を記述する位置を適切な位置にしない限り、coutの実体であるstreambufの挿入演算子(<<)は、char *しか受けつけず、コンパイルエラーとなる。

> cout << str.c_str() <<endl;//(5)
c_strはワイド文字列のデータ本体をwchar_t *で返すのみ。"test\0"を返す訳ではない。つまり、
 cout << "t\0e\0s\0t\0\0\0" <<endl;//(5)
と書いたのと等しく、更に言えば
 cout << "t" <<endl;//(5)
と等しい。

この回答への補足

#2さんの補足欄のさらに補足です。
あちこち書く場所が飛んでしまって申し訳ありません。

> 文字化けは、端末のエンコードをEUC-JPに変更したら直りました。
半分嘘でした。
どうやら、/etc/locale.gen の ja_JP.EUC-JP のコメントアウトをはずしてlocale-gen した場合にそうなったようです。

ja_JP.UTF-8 の部分だけコメントアウトしてlocale-genし、

コード中にlocale("japanese") を locale("ja_JP.UTF-8") に変更したところ、例外は発生しなくなりました。が、やはり wcout << L"testテスト" の表示(端末のエンコード UTF8) が

test???

になってしまいます。端末のエンコードの問題ではなさそうです。

補足日時:2007/01/11 15:45
    • good
    • 0
この回答へのお礼

詳しいご解説ありがとうございます。
質問のソースコードでうまく行かない理由がよくわかりました。
さっそくwprintfにてフォーマット指定子%lsを使って表示を試みたのですが、日本語を含む場合の出力で文字化けしてしまいました。

ソースコード UTF8
端末のエンコード UTF8

で統一しており、perl で print "testてすと"; 等するとちゃんと端末上に文字化けせずに表示されるのですが・・。

お礼日時:2007/01/11 13:39

(1)~(3)に関しては、#1の方の回答のとおりです。



(4)がコンパイルエラーになる理由ですが、std::coutすなわちstd::basic_ostream<char, std::char_traits<char> >型に対して多重定義されている<<の右オペランドは、const std::basic_string<char, std::char_traits<char>, std::allocator>&型(cons std::string&)であって、const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator>&型(const std::wstring&)ではありません。多重定義されていないわけですから、当然エラーになります。

(5)に関しても同様で、右オペランドにconst char*を取る<<演算子は多重定義されていますが、const wchar_t*を取るものは課定義されていないため、const void*に変換されてしまいます。(表示されないのは変です。アドレス値が出るはずです。ただし、変なファセットを持つロケールをimbueしていない場合)

この回答への補足

お礼欄の補足です。
> 再度試してみましたがやはり何も表示されませんでした。
は wprintf した後の cout が表示されないというよく分らない現象のせいで、wprintfを全てコメントアウトしたら表示されました。

文字化けは、端末のエンコードをEUC-JPに変更したら直りました。
本当はUTF-8で吐いてほしいのですが・・

そこで、UNIXの話になってしまうのですが、locale("japanese")を含むプログラムが実行可能であったマシンの、/etc/locale.gen
ja_JP.UTF-8 UTF-8
をコメントアウトし、
# locale-gen
したら

terminate called after throwing an instance of 'std::runtime_error'
what(): locale::facet::_S_create_c_locale name not valid
Aborted

が表示されるようになってしまいました。現在その原因の調査中です。

補足日時:2007/01/11 15:22
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
コンパイルエラーの理由、よくわかりました。
> 表示されないのは変です。アドレス値が出るはずです
については再度試してみましたがやはり何も表示されませんでした。

ロケール・imbueをキーワードに色々検索し、以下のページを参考に

#include <string>
#include <iostream>
#include <locale>
using std::wstring;
using std::wcout;
using std::endl;
using std::locale;

int main() {
 wstring wstr = L"testテスト";
 wprintf(L"%i \n", wstr.length());
 wprintf(L"%ls \n", wstr.c_str());
 
 wcout.imbue(locale("japanese"));
 wcout << wstr <<endl;
 wcout << L"testてすと" << endl;
}
をコンパイルして実行したところ、

7
test???
test???
test???

と文字化けを起こしてしまうのですが、原因がどうもわかりません。
端末の文字エンコードは UTF-8,ソースコードもUTF-8です。

また、このプログラムを異なる計算機(OS,gccのバージョンは同じです)で実行してみたところ、

terminate called after throwing an instance of 'std::runtime_error'
what(): locale::facet::_S_create_c_locale name not valid
Aborted

という例外を吐いてアボートしてしまいます。
環境がほとんど同じだけに、原因がわからずここでも躓いています。
locale("japanese") が通るように何か(環境の設定,ライブラリ等)他にしなければいけないことがあるのでしょうか?

お礼日時:2007/01/11 13:50

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!