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進数で表示される。
#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)にしてみた。
// 略
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の方でエラーが発生した。
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つの整数値で扱えるようにすることを目指した」ことに起因する。
ちなみにUnicode対応表を見ると、UTF-16として3042が割り当てられているのがわかる。
VCのUTF-8ファイル対応
Visual Studio で UTF-8 で C++ を書いたら心が折れそうになった件 - Hikware.Tech
こちらにあるように/source-charset:utf-8
とすればutf-8を読み込めるが、なかなか悲しい現実が待ち受けています。