VisualStudioと文字コード 少し進展

アバター
MoNoQLoREATOR
記事: 284
登録日時: 14年前
住所: 東京

VisualStudioと文字コード 少し進展

投稿記事 by MoNoQLoREATOR » 10年前

前回はみなさん沢山コメントくださってありがとうございます。
わたくしあれからいろいろと実験してみまして。

いろいろテストやってみて、どうやらVSさんはコンパイル時に文字列リテラルをShift-JISに変換してて、ついでに日本語版WindowsコンソールウィンドウはShift-JISがデフォルトなんだなってことがわかりました。…ってところまでは良いのですが、じゃあutf-8のファイル扱いたいときとかどうすんだよって話ですよ。ユーザーが用意したファイルの文字コードを判別して変換して、さらにユーザーのコンソールウィンドウの現在の文字コードを調べてそれに変換して出力なんてのをやる必要があるとかどんだけめんどくさいんですか。まあ、日本人開発者は日本人にだけソフト配布してりゃいいんだよって考えなのでしょう。utf-8以外の文字コードが全て滅びれば楽になるのに。ついでに世界中の言語が英語とSwiftだけになればもっと楽で素敵な世界がおっと誰か来たようだ。

コンパイル時の文字列リテラルを指定できないのか?と思って調べてみると、それっぽい内容を扱っているサイトを見つけました。

どうやら

「Unicode文字セットを使用する」を選択した場合:
・Windows.hとかの関数はWの方が選択されるようになる
・TCHAR, tstring, _T()などのマクロはワイド文字の方が選択されるようになる
・コンパイル時、文字列リテラルはutf-16に変換される

「マルチバイト文字セットを使用する」を選択した場合:
・Windows.hとかの関数はAの方が選択されるようになる
・TCHAR, tstring, _T()などのマクロはマルチバイト文字の方が選択されるようになる
・コンパイル時、文字列リテラルはShift-JISに変換される

ということらしい。

実験を3つ行ってみました。

準備したファイル

CODE:

alphabet
日本語
Shift-JIS
ソースコード
► スポイラーを表示
結果

CODE:

alphabet:
mutched!

vねrf(←文字化け)
mutched!

utf-8で文字化けしてるのはutf-8をShift-JISに変換せずにコンソールに渡してるから当たり前ですね。
ということはうまくいってないのはutf-16のときですね。どうしてでしょうね。
ちなみにBOMありも試してみましたがダメでした。

まあ、ぶっちゃけ文字列リテラルを使わなければいいだけの話なんですけどね!!

アバター
みけCAT
記事: 6734
登録日時: 14年前

Re: VisualStudioと文字コード 少し進展

投稿記事 by みけCAT » 10年前

なんでソースコードにはmatchedって書いてあるのに、結果はmutchedなんだ…不思議だなあ…
WebArchive

YuO
記事: 947
登録日時: 14年前

Re: VisualStudioと文字コード 少し進展

投稿記事 by YuO » 10年前

MoNoQLoREATOR さんが書きました:「Unicode文字セットを使用する」を選択した場合:
・Windows.hとかの関数はWの方が選択されるようになる
・TCHAR, tstring, _T()などのマクロはワイド文字の方が選択されるようになる
・コンパイル時、文字列リテラルはutf-16に変換される
VC++において,文字列リテラルは原則としてコンパイル環境のシステムの文字コードとしての文字列に,
ワイド文字列リテラルは常にUTF-16の文字列に,それぞれ置き換えられます。

文字列リテラル: プリフィックス無しの,"xxx"という文字列
ワイド文字列リテラル: Lプリフィックスがついた,L"xxx"という文字列
MoNoQLoREATOR さんが書きました:utf-8で文字化けしてるのはutf-8をShift-JISに変換せずにコンソールに渡してるから当たり前ですね。
ということはうまくいってないのはutf-16のときですね。どうしてでしょうね。
C/C++のI/O系の仕様です。
  • 入力系は読み込んだマルチバイト文字列をワイド文字列に変換してプログラムに返す
  • 出力系はプログラムから受け取ったワイド文字列をマルチバイト文字列にして出力する
  • 入出力の変換にはプログラムのロケールが使用される
VC++では,UTF-16のファイルをワイド文字列入出力関数・クラスでは正常にアクセスできません。
上記のように,マルチバイト→ワイドの文字コード変換がかかるため,文字列が壊れます。
通常の文字列入出力関数・クラスのバイナリモードでアクセスするのが,おそらく唯一の方法になります。
# これは他の処理系でも同じだと思います。

ワイド文字列リテラルの出力が化けるのは,ロケールを指定していない(=Cロケールになっている)ことが理由です。
Cではsetlocaleを,C++ではstd::wcout.imbueを使って,ロケールを指定する必要があります。
ロケール名は処理系依存ですが,環境に従うロケールを意味する""を使うのが簡単だと思います。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 14年前
住所: 東京

Re: VisualStudioと文字コード 少し進展

投稿記事 by MoNoQLoREATOR » 10年前

>>みけCATさん
うっかり間違えました。よくやっちゃうんです。

>>YuOさん
なるほど、文字列を読み込んだ時点でShift-JISからutf-16に変換する処理が強制的に実行されてしまうのですね。
文字エンコードを自動的に判別してutf-16に変換してくれるのなら嬉しかったのに…。さすがにそこまでは高機能すぎますね。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 14年前
住所: 東京

Re: VisualStudioと文字コード 少し進展

投稿記事 by MoNoQLoREATOR » 10年前

実験してきました。
std::cout.imbue(std::locale(""));
を追加しても、utf-8の方は文字化けしたままでした。
► スポイラーを表示
結果

CODE:

alphabet:
matched!

日本語:
matched!
大成功!

YuO
記事: 947
登録日時: 14年前

Re: VisualStudioと文字コード 少し進展

投稿記事 by YuO » 10年前

MoNoQLoREATOR さんが書きました:実験してきました。
std::cout.imbue(std::locale(""));
を追加しても、utf-8の方は文字化けしたままでした。
""は日本語Windowsでは日本語環境,文字コード関係ではCP932を指定しますから,当然文字化けします。
たぶん,"ja_JP.65001"とか".65001"のような,UTF-8のコードページを指定する必要があると思います。
オフトピック
リファレンス読んだだけで実際に実行はしていません。
MoNoQLoREATOR さんが書きました:

CODE:

std::wifstream ream("utf-16.txt", std::ios::binary);
MoNoQLoREATOR さんが書きました:どうやらalphabetのそれぞれの間に\0が挟まっている模様。どうしてこうなった。utf-32じゃねえんだぞ。
std::wifstream (ワイド文字列を扱う) ではなく,std::ifstreamを使う必要があります。
# 「通常の文字列入出力関数・クラス」はそういう意味。

ISLe
記事: 2650
登録日時: 14年前

Re: VisualStudioと文字コード 少し進展

投稿記事 by ISLe » 10年前

MoNoQLoREATOR さんが書きました:しかたがないのでfopenやfreadを使って読み込みました。
VC++なら入出力の文字コード指定ができますよ。
UNICODE,UTF-16LE,UTF-8から選べます。
#UNICODEはUTF-16BEだろうか。

とりあえずワイド文字の内部的な文字コードを決め打ちしてはいけないと思います。
wchar_tはVC++は16ビットだけどgccだと32ビットだったりするし。
読み込むときに変換する、書き出すときに変換する、というのは常に必要なことです。
C/C++に限ったことでもないかと。