Qtの基礎 - 文字コード

提供:MochiuWiki : SUSE, EC, PCB
2025年1月6日 (月) 06:40時点におけるWiki (トーク | 投稿記録)による版 (→‎概要)
ナビゲーションに移動 検索に移動

概要

Qtでは、標準の文字コードはUTF-16である。

例えば、Visual StudioとQtを連携する場合、ソースコードのファイルの文字コードはUTF-8であるため、文字コードの変換が必要となる。
(変換しない場合は文字化けする)


QStringの内部表現

QStringの内部表現は、プラットフォームに関係なく常にUTF-16で文字列を保持する。

  • 内部表現
    QString str = "こんにちは";  // 内部的にはUTF-16として保存される
    
  • 確認方法
    QString str = "こんにちは";
    QByteArray utf16 = str.toUtf16();  // 内部表現のUTF-16を直接取得
    QByteArray utf8 = str.toUtf8();    // UTF-8に変換
    
    qDebug() << "UTF-16 size:" << utf16.size();  // UTF-16のバイト数
    qDebug() << "UTF-8 size:" << utf8.size();    // UTF-8のバイト数
    


  • データの流れ
    // 入力 (UTF-8) -> QString (UTF-16) -> 出力 (任意の文字エンコーディング)
    QByteArray utf8_input = "こんにちは";           // UTF-8データ
    QString str = QString::fromUtf8(utf8_input);  // 内部でUTF-16に変換
    QByteArray output = str.toUtf8();             // 必要に応じてUTF-8に変換して出力
    <br>
    QStringは常にUTF-16で保持する仕様であるためUTF-8として保持する場合はQByteArrayを使用する必要がある<br>
    <br>
    <u>注意</u><br>
    <u>QByteArrayは文字単位の操作が難しい</u><br>
    <u>またUnicode正規化等の操作ができない</u><br>
    <br>
    <u>QByteArrayは文字列長の取得が正確でない可能性がある</u><br>
    <br>
    QFile::writeメソッドはQByteArrayまたはconst char*を期待するためQStringを直接指定する場合はエラーになる<br>
    QStringからの暗黙の型変換は許可されていない<br>
    <br>
    QTextStreamを使用する場合はエンコーディングを明示的に制御できる<br>
    バイナリモードで書き込む場合はエンコーディングの制御が完全に開発者の責任になる<br>
     <syntaxhighlight lang="c++">
     QFile file("test.txt");
     QString str = "こんにちは";
     
     if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        // 方法 1 : QTextStreamを使用 (推奨)
        QTextStream out(&file);
        out.setEncoding(QStringConverter::Utf8);  // UTF-8を指定
        out << str;
     
        // 方法 2 : 直接バイト列として書き込む
        // file.write(str.toUtf8());
     
        file.close();
     }
    


選択の基準

  • QStringを使用する場合
    テキスト処理や文字列操作が必要な場合
    Unicode関連の操作が必要な場合
    クロスプラットフォームの互換性が必要な場合
    アプリケーション内部の処理では、QString (UTF-16) を使用する。

  • QByteArrayを使用する場合
    バイナリデータとして扱う場合
    エンコーディングを維持したまま保存、あるいは、送信する場合
    メモリ使用量を最小限にする場合
    外部とのやり取り (ファイルI/O、ネットワーク通信等) では、必要に応じてQByteArray (UTF-8) を使用する。



QTextCodecクラスについて

Qt 5 / Qt 6の違い

Qt 6では、QTextCodecクラスの使用方法が変更された。
Qt 6ではQTextCodecクラスのインクルードはサポートされておらず、代わりに文字エンコーディングの処理がQStringConverterクラスに移行された。
これにより、より柔軟で効率的な文字列変換が可能になった。

  • Qt 6の場合
 #include <QStringConverter>


例えば、Qt 5において、以下に示すようなコードがあるとする。

 // Qt 5の場合
 
 #include <QTextCodec>
 
 // Shift-JISからUTF-8へデコードする場合
 QByteArray SJISdata = "あいうえお";
 QTextCodec *codec = QTextCodec::codecForName("Shift-JIS");
 QString str       = codec->toUnicode(SJISdata);
 
 // UTF-8からShift-JISへエンコードする場合
 QString    str  = "あいうえお";
 QByteArray data = str.toUtf8();  // 文字列をバイト列へ変換
 
 QTextCodec *codec;  // エンコードオブジェクト
 codec           = QTextCodec::codecForName("Shift-JIS");
 encodedPostData = codec->fromUnicode(data);


Qt 6では、以下に示すように書き換えることができる。

 // Qt 6の場合
 
 #include <QStringConverter>
 
 // Shift-JISからUTF-8へデコードする場合
 QByteArray SJISdata = "あいうえお";
 QStringDecoder decoder("Shift-JIS");
 QString str         = decoder(SJISdata);
 
 // UTF-8からShift-JISへエンコードする場合
 QString    str  = "あいうえお";
 QByteArray data = str.toUtf8();  // 文字列をバイト列へ変換
 
 QStringEncoder encoder("Shift-JIS");
 QString encodeStr = encoder(data);


この変更は、QtのコアAPIをより現代的で効率的なものにするための一環である。
新しいAPIは、以前のものと比較してより柔軟性が高く、パフォーマンスも向上している。

Qt 6でQTextCodecクラスを使用する場合

Qt 6において、以前のQTextCodecクラスを使用することもできる。

  • CMakeを使用する場合
 find_package(Qt6 REQUIRED COMPONENTS Core5Compat)
 
 target_link_libraries(mytarget PRIVATE Qt6::Core5Compat)


  • QMakeを使用する場合
 QT += core5compat


ソースコードファイルにおいて、QTextCodecクラスをインクルードする。

 #include <QTextCodec>
 
 QTextCodec *codec = QTextCodec::codecForName("UTF-8");
 QString str = codec->toUnicode(byteArray);



UTF-8から他の文字コードへの変換

QString::fromLocal8Bit()

Visual StudioとQtを連携する場合、文字列リテラルはソースコードのファイルの文字コードに関わらず、
ロケールの文字コード(WindowsならShift-JIS)に変換してコンパイルされるため、
現在のロケールに合わせた文字コードで変換するQString::fromLocal8Bit()を使用する。

 QString strU8 = QString::fromLocal8Bit("テスト");
 ui->label->setText(strU8);


QString::fromUtf8()

Visual Studio 2015以降とQtを連携する場合、Unicode文字列リテラルに対応しているため、
接頭辞u8を付加することで、UTF-8のまま(ロケールの文字コードに変換されることなく)コンパイルすることができる。

 QString strU8 = QString::fromUtf8(u8"テスト");
 ui->label->setText(strU8);


pragmaの指定

Visual Studio 2013以前とQtを連携する場合、pragmaを指定することで、文字列リテラルをUTF-8に変換することができる。

 #pragma execution_character_set("utf-8")


その他の方法

以下のように、クラスに静的メソッドを定義して使用する方法でもよい。

 std::string MainWindow::UTF16_to_UTF8(const QString &src)
 {
    return src.toUtf8().toStdString();
 }



UTF-16から他の文字コードへの変換

現在のロケールへの変換

UTF-16から現在のロケール(Windowsの場合はShift-JIS)へ変換する。

 std::string MainWindow::UTF16_to_Locale(const QString &src)
 {
    return src.toLocal8Bit().toStdString();
 }


Shift-JISへの変換

UTF-16からShift-JISへ変換する。

 std::string MainWindow::UTF16_to_SJIS(const QString &src)
 {
    QTextCodec *codec = QTextCodec::codecForName("Shift-JIS");
    QByteArray encoded = codec->fromUnicode(src);
 
    return encoded.toStdString();
 }


EUC-JPへの変換

UTF-16からEUC-JPへ変換する。

 std::string MainWindow::UTF16_to_EUC(const QString &src)
 {
    QTextCodec *codec = QTextCodec::codecForName("EUC-JP");
    QByteArray encoded = codec->fromUnicode(src);
 
    return encoded.toStdString();
 }



Shift-JISからUTF-16への変換

Shift-JISからUTF-16へ変換する。

 QString MainWindow::SJIS_to_UTF16(const std::string &src)
 {
    QTextCodec *codec = QTextCodec::codecForName("Shift-JIS");
 
    return codec->toUnicode(src.c_str());
 }



EUC-JPからUTF-16への変換

EUC-JPからUTF-16へ変換する。

 QString MainWindow::EUC_to_UTF16(const std::string &src)
 {
    QTextCodec *codec = QTextCodec::codecForName("EUC-JP");
 
    return codec->toUnicode(src.c_str());
 }