Visual C++(cl.exe)で UTF-8 のファイルをコンパイルする
日本語版 Windows では、デフォルトの文字コードとして Shift-JIS(CP932)が広く使われています。
この Shift-JIS、2バイト目が ASCII コードと同じ、という文字がいくつか存在します。
例えば「ソ」の Shift-JIS でのコードは16進数で835c
で、2バイト目の5c
はバックスラッシュ(または¥マーク)と同じです。
このため、例えば
#include <iostream> int main() { char str[] = "ソ"; std::cout << str << std::endl; return 0; }
上記を Shift-JIS で保存してコンパイルしてみます。
Visual Studio では問題なくコンパイル・実行できるのですが、gcc(g++)では、
error: missing terminating " character char str[] = "▒\"; ^
怒られました。
「ソ」の2バイト目と、それに続くダブルクォーテーションがエスケープシーケンスとして処理されてしまうため、文字列の終わりを表すはずのダブルクォーテーションが文字列に含まれてしまい、文字列が終わっていないというエラーになってしまうわけです。
Shift-JIS に対応していないツールでは、似たような問題はしょっちゅう顕在化します。
いわゆる「ダメ文字」問題ですね。
「ダメ文字」でググるといろいろ情報が出てくると思います。
てことで本題。
ファイルの文字コードに Shift-JIS なんて使うのはやめて、UTF-8 を使うようにしよう!
さっきのコードを UTF-8 *1 で保存して、Visual Stuidio(cl.exe)でコンパイルして実行してみます。
コンパイルは通りますが、実行すると、
繧ス
文字化けしました。
上のコードのコンパイル時には特に何も言わませんが、文字列の部分を、
char str[] = "あいうえお";
こう書き換えてコンパイルしてみると、
warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。 error C2001: 定数が 2 行目に続いています。 error C2143: 構文エラー: ';' が 'std::cout' の前にありません。
なんかいろいろ怒られました。
どうやら、ソースの文字コードが実際には UTF-8 であるにも関わらず、Shift-JIS としてコンパイルされているみたいですね。
UTF-8 での「お」の最後のバイトと、それに続くダブルクォーテーションが、Shift-JIS として解釈されることにより、合体して1文字になってしまっているのでしょう。
さて、どうしたものか。
こそっと脚注入れましたが、上の例は UTF-8 の BOM なしのファイルで試した結果です。
BOM ありの UTF-8 なら、エラーも文字化けも起きません。
Visual Studio で UTF-8 のファイルを扱う場合、BOM ありにするのがもっとも単純な解決法のようです。
しかし、単なるテキストに過ぎないソースファイルにバイナリコードを含めるのはやっぱり何かヘンな気がしますし、BOM 付きだとうまく動かないツールもあったりするなど、単に BOM を付ければ万事解決、というわけにはいかないとも思うのです。
てなわけで、もうちょっと調べてみました。
要は コンパイラにソースの文字コードを伝えられればいいわけですよね。
cl.exe の場合は、BOM がこの役割を担っているわけですが、こういうのってオプションで指定できることが多いものです。
MS製ツールは/?
オプションでヘルプが見れますので、cl /?
の出力を眺めてみます。
そしたらこんなオプションを見つけました。 *2
/source-charset:|.nnnn は、ソース文字セットを設定します /execution-charset: |.nnnn は、実行文字セットを設定します /utf-8 は、ソースおよび実行文字セットを UTF-8 に設定します /validate-charset[-] では、有効な文字に対してのみ UTF-8 ファイルが検証されます
MSDN にも記事がありました(2017年3月4日現在、日本語のものはないようです)。
-source-charset (Set Source Character Set)
-execution-charset (Set Execution Character Set)
-utf-8 (Set Source and Executable character sets to UTF-8)
-validate-charset (Validate for compatible characters)
記事の対象バージョンは Visual Studio 2015 となっています。
Visual Studio 2013 以前のバージョンにはこれらのオプションは存在しません。
4つのそれっぽいオプションのうち、今回の目的に合致しそうなのは、/source-charset:utf-8
です。これは、ソースファイルの文字コードを指定します。
/execution-charset:utf-8
は、指定すると、コンパイルされたプログラムの出力が UTF-8 になります。コマンドプロンプトは デフォルトでは CP932 で動いており、UTF-8 での出力は文字化けするため、今回はこのオプションは使えません。コマンドプロンプト上でchcp 65001
を実行することで UTF-8 で動くようにはできますが、この時のフォントに日本語フォントの指定ができない *3 ため、表示するものが無くて結局文字化けします。/utr-8
は短くて便利かと思いきや、/execution-charset:utf-8
が指定されてしまうため使えません。/validate-charset
は、ソース中の文字が UTF-8 として表すことができるかどうかをチェックします。上3つのいずれかを指定すれば、勝手に指定されたことになります。特に気にする必要はなさそうです。
てことでオプション指定してコンパイル。
cl /source-charset:utf-8 source.cpp
これで、BOM なし UTF-8 でもコンパイルでき、文字化けも起こりません。
なお、Visual Studio のプロジェクトの設定には該当する項目がありません。
IDE でこれらオプションを設定するには、プロジェクトのプロパティから [C/C++] → [コマンドライン] → [追加のオプション] に指定します。
ということで、方針としては、
- BOM ありで不都合が無ければ、BOM ありの UTF-8 を使う
- BOM を付けたくない場合、Visual Studio 2015 以降なら
/source-charset
が使える - 2013 より前ならおとなしく BOM 付けるか Shift-JIS に甘んじるか……
こんな感じになると思います。
当初は BOM ありにすりゃおk、ってことを備忘録として書いておこうと思ってたんですが、オプションの存在を知りましたので、これも合わせて記事にしてみたら、予想外に長くなってしまいました。
文字列めんどくさい(´・ω・`)