【问题标题】:QString to unicode std::stringQString 到 unicode std::string
【发布时间】:2014-04-03 13:56:06
【问题描述】:

我知道有很多关于将QString 转换为char* 的信息,但我仍然需要对这个问题进行一些澄清。

Qt 提供QTextCodecs 将QString(内部以unicode 存储字符)转换为QByteArray,允许我检索char*,它表示某些非unicode 编码的字符串。但是当我想得到一个unicodeQByteArray时应该怎么做呢?

QTextCodec* codec = QTextCodec::codecForName("UTF-8");
QString qstr = codec->toUnicode("Юникод");
std::string stdstr(reinterpret_cast<const char*>(qstr.constData()), qstr.size() * 2 );  // * 2 since unicode character is twice longer than char
qDebug() << QString(reinterpret_cast<const QChar*>(stdstr.c_str()), stdstr.size() / 2); // same

如我所料,上面的代码打印出“Юникод”。但我想知道这是否是获取QString 的unicode char* 的正确方法。特别是,这种技术中的reinterpret_casts 和大小算术看起来很丑。

【问题讨论】:

  • @ratchetfreak 你的意思是 UTF8 和 Unicode 是平等的?
  • UTF8 是字节大小的 unicode 格式,内部 QString 使用 UTF16,你也可以获取data()
  • QString 已经是“юникодед”。所以只需致电str.toStdWString()std::string 不是为存储 16 位字符而设计的。
  • "你的意思是 UTF8 和 Unicode 是相等的" 不,你使用 Unicode 这个词是错误的。 Unicode 不是一种编码,它是一种标准,因此谈论“Unicode std::string”没有任何意义。字符串本身不能与 Unicode 兼容。 std::string 将具有特定的“字符”类型(通常为 8 位或 16 位宽),并且将具有特定的编码(通常为 16 位字符的 UCS-2 或 UTF-16)。 UCS-2 和 UTF-16 之间的最大区别在于 UCS-2 是固定宽度的:每个“字符”一个代码点。在 UTF-16 中,每个代码点可能有多个“字符”。
  • 短语“unicode QByteArray”没有意义。相当于说“wakalixes QByteArray”。字节数组可以携带一些 8 位编码的文本数据,例如 Latin1 (ISO/IEC 8859-1) 或 UTF-8 等。如果你想要一个 8 位编码的字节数组作为字符串的表示, 你需要知道这样一个数组的用户期望什么编码。只有这样你才能决定如何对字符串进行编码。

标签: qt unicode encoding qstring


【解决方案1】:

以下内容适用于 Qt 5。Qt 4 的行为有所不同,并且在实践中被破坏了。

您需要选择:

  1. 您是否想要 8 位宽 std::string 或 16 位宽 std::wstring 或其他类型。

  2. 您的目标字符串需要什么编码?

在内部,QString 存储 UTF-16 编码数据,因此任何 Unicode 代码点都可以用一两个 QChars 表示。

常见情况:

  • 本地编码的 8 位 std::string(如:系统语言环境):

    std::string(str.toLocal8Bit().constData())
    
  • UTF-8 编码的 8 位 std::string:

    str.toStdString()
    

    这相当于:

    std::string(str.toUtf8().constData())
    
  • UTF-16 或 UCS-4 编码 std::wstring,分别为 16 位或 32 位宽。 16 位和 32 位编码的选择由 Qt 完成,以匹配平台的宽度wchar_t

    str.toStdWString()
    
  • C++11 的 U16 或 U32 字符串 - 从 Qt 5.5 开始:

    str.toStdU16String()
    str.toStdU32String()
    
  • UTF-16 编码的 16 位 std::u16string - 仅在 Qt 5.4 之前需要此 hack:

    std::u16string(reinterpret_cast<const char16_t*>(str.constData()))
    

    此编码不包括字节顺序标记 (BOM)。

在转换之前将 BOM 预先添加到 QString 本身很容易:

QString src = ...;
src.prepend(QChar::ByteOrderMark);
#if QT_VERSION < QT_VERSION_CHECK(5,5,0)
auto dst = std::u16string{reinterpret_cast<const char16_t*>(src.constData()),
                          src.size()};
#else
auto dst = src.toStdU16String();

如果您希望字符串很大,您可以跳过一份:

const QString src = ...;
std::u16string dst;
dst.reserve(src.size() + 2); // BOM + termination
dst.append(char16_t(QChar::ByteOrderMark));
dst.append(reinterpret_cast<const char16_t*>(src.constData()),
           src.size()+1);

在这两种情况下,dst 现在可以移植到具有任一字节顺序的系统。

【讨论】:

    【解决方案2】:

    使用这个:

    QString Widen(const std::string &stdStr)
    {
        return QString::fromUtf8(stdStr.data(), stdStr.size());
    }
    
    std::string Narrow(const QString &qtStr)
    {
        QByteArray utf8 = qtStr.toUtf8();
        return std::string(utf8.data(), utf8.size());
    }
    

    在所有情况下,你 should have utf8 在 std::string 中。

    【讨论】:

    • 为什么从Utf8调用时需要stdStr.size()?这是否会导致将终止的 null 存储在 QString 中?否则,fromUtf8 似乎默认读取到终止 null...
    【解决方案3】:

    您可以使用以下方法从 UTF-16 编码的 QString 中获取 QByteArray:

    QTextCodec *codec = QTextCodec::codecForName("UTF-16");
    QTextEncoder *encoderWithoutBom = codec->makeEncoder( QTextCodec::IgnoreHeader );
    QByteArray array  = encoderWithoutBom->fromUnicode( str );
    

    这样你会忽略开头的 unicode 字节顺序标记 (BOM)。

    您可以将其转换为 char * like:

    int dataSize=array.size();
    char * data= new char[dataSize];
    for(int i=0;i<dataSize;i++)
    {
        data[i]=array[i];
    }
    

    或者简单地说:

    char *data = array.data();
    

    【讨论】:

    • 没有“unicode 字节数组”之类的东西——请停止使用这个术语,它让每个人都感到困惑。 Unicode 是一种标准,而不是一种编码。有 UTF-16 和 UCS-2,后者是 QString 内部编码的内容。 UCS-2 是代码点 0-0xFFFF 的 UTF-16 子集。由于QString 不能携带超出该范围的代码点,因此您无需执行任何特殊操作即可从QString 中获取UTF-16。只需使用字符串的constData()
    • @KubaOber 使用 constData() 还可以让您在乞讨时获得 BOM,这是一团糟。使用上述方法,您可以获得与字符串相关的 QByteArray,也可以使用不同的编码选项。
    • 您确定QString 存储嵌入的BOM 吗?
    • 是的。你可以看到*.com/questions/3602548/…
    • 其实我刚查过,QString没有带嵌入式BOM。会浪费空间的。此代码将转储 BOM;它没有:QString str1(QStringLiteral("A")); const QChar * p = str1.constData(); while (p-&gt;unicode()) qDebug() &lt;&lt; *p++;