Edited at

文字列はファイル依存?コンパイラ依存?

image001.png


error

warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。

error C2001: 定数が 2 行目に続いています。
error C2143: 構文エラー : ';''for' の前にありません。
warning C4018: '<' : signed と unsigned の数値を比較しようとしました。

Visual C++を使っていてこんなエラーを見たことがあるだろう。

でもこのエラー、文字コードに起因することが原因というのはわかるのだが、ファイルが原因なのか、コンパイラが原因なのかがよくわからなかった。


検証

Clang、VCで以下のコードを実行してみた。

shift_jis、UTF-8、UTF-8N(BOM無し)の3種類でそれぞれファイルを保存してコンパイルを実行させている。

実行すると、文字列がコンソールに16進数で表示される。


main.cpp

#include <iostream>

// int _tmain(int argc, _TCHAR* argv[]) // <- Visual Studio
int main() {
std::string text = "あ";
for (int i = 0; i < text.length(); i++) {
std::cout << std::hex << (int) text[i] << std::endl;
}
// system("pause"); //<-VisualStudioのみ
return 0;
}

今回使った環境は下記

OS
コンパイラ
エディタ

Mac
Clang
XCode,CLion

Win
VisualC++
VisualStudio

結果は下記

コンパイラ
ファイルの文字コード
結果表示

Clang(Mac)
Shift_jis
ffffff82
ffffffa0

Clang(Mac)
UTF-8
ffffffe3
ffffff81
ffffff82

Clang(Mac)
UTF-8N
ffffffe3
ffffff81
ffffff82

VC(Win)
Shift_jis
ffffff82
ffffffa0

VC(Win)
UTF-8
ffffff82
ffffffa0

VC(Win)
UTF-8N
Error

ということは、Clangは、ファイルのコードをそのまま通して、VisualStudioはBOMがあればUTF-8であると認識してShiftJISに変換したうえでコンパイルしているということになる。

追記:yumetodo様より、「gccとmsvcは文字コードの変換機能がある。」と教えていただきました。

※結果表示の先頭にあるffffffは、charが1バイトに対してintが4バイトであるため。


wchar_t

文字列をwstring(wchar_t)にしてみた。


main.cpp

// 略

std::wstring text = L"あ";
// 略

コンパイラ
ファイルの文字コード
結果表示

Clang(Mac)
Shift_jis
Error

Clang(Mac)
UTF-8
3042

Clang(Mac)
UTF-8N
3042

VC(Win)
Shift_jis
3042

VC(Win)
UTF-8
3042

VC(Win)
UTF-8N
Error

今度はClangの方でエラーが発生した。


CLionコンソール

Scanning dependencies of target test_unit

[ 50%] Building CXX object CMakeFiles/test_unit.dir/main.cpp.o
main.cpp:6:27: error: illegal character encoding in string literal
std::wstring text = L"<82><A0>";
^~~~~~~~
1 error generated.
make[3]: *** [CMakeFiles/test_unit.dir/main.cpp.o] Error 1
make[2]: *** [CMakeFiles/test_unit.dir/all] Error 2
make[1]: *** [CMakeFiles/test_unit.dir/rule] Error 2
make: *** [test_unit] Error 2

ようは、元の文字列をワイド文字列に変換出来ないということらしい。

エラーが無い場合すべて3042になっているのは、ワイド文字列が「1文字を1つの整数値で扱えるようにすることを目指した」ことに起因する。


ワイド文字 - Wikipedia


ちなみにUnicode対応表を見ると、UTF-16として3042が割り当てられているのがわかる。

Unicode

image002.png


VCのUTF-8ファイル対応

Visual Studio で UTF-8 で C++ を書いたら心が折れそうになった件 - Hikware.Tech

こちらにあるように/source-charset:utf-8とすればutf-8を読み込めるが、なかなか悲しい現実が待ち受けています。