EUC-JP環境でQt4を使って文字化けした件

今日はCentOS7のEUC-JP環境でQt4ベースの画面を作っていてハマった問題を紹介します。

QButtonのテキストにNEC特殊文字(①②③やⅠⅡⅢ)を含めると表示が文字化けしてしまいました。QButtonの内部ではラベル文字列をUTF16で管理していますが、NEC特殊文字は純粋なEUC-JPでは定義されないので、文字コード変換した結果がおかしくなっているのだと思います。JAVAを使っていて類似の問題を経験した時にはエンコーディングとして EUC_JP_Solaris を指定することで解決しました。Qtでも同様の設定があるだろうと高を括って調べましたが見あたりません。仕方がないのでEUC-JPをUTF16に変換する処理を自前で持ち、変換した結果をQButtonに渡すことにします。

結局、以下のようにすることで文字化けを回避できました。変換エンジンはiconv。得られたUTF16文字列を直接QButtonに渡す手段も見当たらなかったので、1文字ずつQStringに追加しています。

#include <boost/foreach.hpp>
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>
#include <cstring>
#include <iconv.h>
using namespace std;

// NEC拡張文字を含むEUC-JP文字列をUTF-16文字列に変換する
static int convert_to_unicode(char *in, size_t in_size, char *out, size_t out_size) {
    iconv_t context = iconv_open("UTF-16", "EUC-JP-MS");
    iconv(context, &in, &in_size, &out, &out_size);
    iconv_close(context);
    return 0;
}

// メイン処理
int main(int argc, char **argv)
{
    // Main Window
    QApplication app(argc, argv);
    QWidget      window;
    QVBoxLayout  layout;
    QPushButton  button1("EUC-JP [③Ⅲ3]"); // 文字化けする
    QPushButton  button2("EUC-JP [③Ⅲ3]"); // 文字化けする
    layout.addWidget(&button1);
    layout.addWidget(&button2);
    window.setLayout(&layout);

    // Button #1 : UTF16文字コードを直接セットする
    QString name1("UTF16 [");
    name1.append(QString(0x2462)); // ③
    name1.append(QString(0x2162)); // Ⅲ
    name1.append(QString(0xFF13)); // 3
    name1.append("]");
    button1.setText(name1);

    // Button #2 : iconvでUTF16に変換した文字列をセットする
    QString name2("");
    char in[] = "iconv [③Ⅲ3]"; // EUC-JP-MS
    char16_t out[128] = { 0 };    // UTF-16
    convert_to_unicode(in, strlen(in), (char*)out, sizeof(out));
    BOOST_FOREACH(auto c, u16string(out)) name2.append(QString(c));
    button2.setText(name2);

    window.show();
    return app.exec();
}

LANG=ja_JP.eucJP が前提の世界で物作りをしていると、UTF-8を前提としている最近のインフラとの間でミスマッチが起こりやすいのです。