Qt とソースファイルの文字コード

先日いただいたコメントをきっかけに Windows における Qt とソースコードの文字コードについてまとめてみました。不足している内容もあるかと思いますので、コメント等で指摘いただければ追記していきます。

Qt の推奨

Qt では Qt4 の時からソースファイルの文字コードは UTF-8 で書くことを前提としています。メッセージは英語(もしくは ID)で埋め込み、日本語などを表示する場合は基本的には tr() でローカライゼーションの仕組みを使って変換させることを想定しています。

以前は Latin1 を推奨していて、UTF-8 文字列を埋め込む場合には \x でエンコードすることを推奨していました。しかし、Unicode / UTF-8 が一般的となってきたので緩和された形でしょうか。とはいえ、余裕があればエンコードした形式の方がトラブルは起こりにくいです。メッセージをソースから把握するのは難しくなりますが。

ユーザーの要望

しかし、ちょっとしたアプリの作成する際に翻訳を用意したり、エンコードするのは面倒なので、日本語文字列をそのまま埋め込みたいという状況があります。

Linux や Mac などでは UTF-8 なロケールの利用が一般的となったため、UTF-8 でソースを記述しておけば困ることはほとんど無くなりました。しかし、Windows では Shift JIS(CP932) が日本語のコーデックであること、Visual Studio が文字列リテラルの文字コードを変換してコンパイルすることから様々な工夫が必要です。

文字列リテラルの扱い

私が把握しているところでは、文字列リテラルが文字化けするのは主に以下の二つです。

  • QString::QString(const char *)
  • QDebug()::operator<<(const char *)

それぞれ Qt 4 と Qt 5 とで挙動が異なりますので、別々に説明します。

Qt 4

Qt 4 では文字列リテラルを QString へ変換するには主に以下のメソッドを使用します。

  • QString::fromAscii()
    QTextCodec::setCodecForCString() で codecForCString 設定済みの場合はそれを、そうで無い場合は QString::fromLatin1() を使用して char * を QString に変換します。ASCII と名乗りつつ、実質は codecForCString に依存します。Qt 4 で特に何も指定せずに char * から QString に変換する場面のデフォルトメソッドです。
  • QString::fromLatin1()
    Latin1(ISO-8859-1) として QString を作成します。いわゆるマルチバイト系の文字列には未対応で、1バイト1文字として扱われます。特に理由が無ければこのメソッドよりも QLatin1String() を使用した方がより高速です。
  • QString::fromLocal8Bit()
    現在のロケールに合わせたコーデックを使用して QString を生成します。多くの場合はこのメソッドを使って QString を生成することになるでしょう。
  • QString::fromUtf8()
    文字列が UTF-8 として QString を作ります。

Qt 4 では QString::QString(const char *)QDebug::operator<<(const char *) は QString::fromAscii() を使用しています。そのため、QTextCodec::setCodecForCString() で適切なコーデックを指定しておくことで、文字化けを避けることができます。

QTextCodec::setCodecForCString(QTextCodec::codecForLocale());

Qt 5

Qt 5 では文字列リテラルを QString へ変換するには主に以下のメソッドを使用します。Qt 4 にあった fromAscii() が無くなっていることに注意してください。

fromAscii() が無くなったため、QString::QString(const char *)QDebug::operator<<(const char *) は fromUtf8() を使うように変更されました。そのため、文字列リテラルを UTF-8 で渡す必要があります。

対策

Windows で Visual Studio を使用する場合、文字列リテラルはソースファイルの文字コードが Shift JIS か UTF-8 であるかにかかわらず、Shift JIS (ロケールの文字コード)に変換されてコンパイルされます。このため、Qt 5 で文字化けを避けるには以下のいずれかが必要です。

  • #pragma execution_character_set(“utf-8”) を使う
    この pragma を使用するとコンパイルしたオブジェクトの文字コードをロケールの Shift JIS ではなく、UTF-8 にすることができます。Visual Studio 2010 SP1 や Visual Studio 2008 SP1 で使用できます。Visual Studio 2012 には含まれていません。また、Visual Studio 2010 ではなく、Windows SDK 7.1 を使用している場合には SP1 が当たらないため、この pragma は使用できません。Visual Studio 2010 を持っていない場合は Express 版でいいのでインストールしてください。なお、Visual Studio 2010 と Windows SDK 7.1 の双方をインストールする場合にはその順番に注意してください。詳しくは “Qt for Windows Requirements” を参照してください。

    #pragma execution_character_set("utf-8")
    
  • QString::fromLocal8Bit() を使う
    文字列リテラルを適切な QString に変換しておけばその後の処理は問題ありません。記述量的には多くなりますが、短いマクロを定義しておくなど工夫することで妥協できる範囲にはなるでしょう。
  • \x でエンコーディングする
    面倒ですが、\x でエンコードしておけば、その内容が Shift JIS へ変換されることはありません。ただし、\u で直接 Unicode のコードポイントを指定した場合は Shift JIS へと変換されてしまいますので注意してください。

    const char *str1 = "あいう"; // Shift JIS
    const char *str2 = "\u3042\u3044\u3046"; // Shift JIS
    const char *str3 = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"; // UTF-8
    

補足1: Qt Creator と UTF-8

Visual Stuido で UTF-8 なソースファイルをビルドするには BOM がファイルの先頭に埋め込まれている必要があります。Qt Creator でソースファイルを作成した場合、デフォルトではその文字コードは UTF-8 になっていますが、BOM は付きません。この動作はオプションで変更できます。

「ツール」→「オプション」でオプションダイアログを表示し、「テキストエディタ」→「動作」タブの「ファイルの文字コード」のところを変更してください。「UTF-8 BOM」で BOM を UTF-8 のファイルでは常に追加するような設定が可能です。

オプションダイアログ

また、編集中のファイルに対しては右クリックからコンテキストメニューを表示して、「保存時に UTF-8 BOM を追加」を選択することで UTF-8 BOM を追加することができます。なお、この項目を選んだだけでは保存はされませんし、バッファーも編集済み状態にはなりませんので、(必要ならば編集を行った上で)別途ファイルの保存を行ってください。

コンテキストメニュー

補足2: qPrintable()

qDebug() は便利ですが、operator<<() を使用するには qdebug.h あるいは QDebug を #include する必要があります。そういうときには qDebug(const char *fmt, ...) の方を使用することがあります。このメソッドや QString::sprintf() などで %s に受け付ける文字列は char * であり、QString ではないため、qstring.toLocal8Bit().constData() のように QString から const char * を作成する必要があります。この toLocal8Bit().constData() を一気に実行してくれるのが qPrintable() です。 ですが、qDebug() や QString::sprintf() の引数はフォーマットは Latin1、%s に対応するのは UTF-8 である必要があります。そのため、日本語版の Windows では qPrintable() を使用すると Shift JIS になり、逆に文字化けの原因となりますので注意してください。

補足3: C++11

C++11 では文字列リテラルのエンコーディングを指定する接頭子が追加されました。その中に UTF-8 を指定する u8 接頭子があります。Visual Studio ではまだサポートされていませんが、将来的にサポートされた場合にはこれを用いるのが一番スマートかもしれません。

const char *str1 = u8"あいう"; // UTF-8

「Qt とソースファイルの文字コード」への2件のフィードバック

  1. Qt(Linux上)でEDA系のソフト開発をしておる者です。
    Windowsで私用の小プログラムを開発しようと思い、VisualStudioを使う位ならQtでもと思い、Windows上でQt環境を入れてみました。ソースコードもさることながら、日本語データの内部処理の方もできるか否かをはじめ、分からない事だらけです。本ブログでも、もう少し日本語処理系の話題を扱って頂けると助かります。

    1. 確かにそういう方面のドキュメントは少ない気がします。
      少し後になりますが、書いてみようと思いますが、具体的に何をするときのコードが知りたいなど有りましたら教えていただけると助かります。

コメントを残す