【问题标题】:QChar::isLetterOrNumber() failsQChar::isLetterOrNumber() 失败
【发布时间】:2012-10-03 14:22:36
【问题描述】:

我想将 QStrings 转换为文件名。因为我希望文件名看起来干净,所以我想用下划线替换所有非字母和非数字。下面的代码应该做到这一点。

#include <iostream>
#include <QString>

QString makeFilename(const QString& title)
{
    QString result;
    for(QString::const_iterator itr = title.begin(); itr != title.end(); itr++)
     result.push_back(itr->isLetterOrNumber()?itr->toLower():'_');
    return result;
}

int main()
{
    QString str = "§";
    std::cout << makeFilename(str).toAscii().data() << std::endl;
}

但是,在我的电脑上,这不起作用,我得到一个输出:

�_

寻找解释,调试告诉我QString("§").size() = 2 > 1 = QString("a").size()

我的问题:

  • 为什么 QString 使用 2 个 QChars 表示“§”? (已解决
  • 您有makeFilename 的解决方案吗?它也适用于中国人吗?

【问题讨论】:

  • 最有可能的是,您正在查看字符串的 UTF-8 编码——“§”在 UTF-8 中使用 2 个字节
  • 注意:我使用现代 Linux 和 g++。我猜它已经是 UTF-8,虽然我不确定。事实上,strlen("§") 返回 2。

标签: c++ string unicode qstring qchar


【解决方案1】:

除了其他人所说的之外,请记住 QString 是一个 UTF-16 编码的字符串。 BMP 之外的 Unicode 字符需要 2 个 QChar 值一起工作,称为代理对,以便对该字符进行编码。 QString documentation 说了这么多:

代码值高于 65535 的 Unicode 字符使用代理对存储,即两个连续的 QChar。

在循环QString 时,您没有考虑到这一点。您正在单独查看每个 QChar,而不检查它是否属于代理对。

试试这个:

QString makeFilename(const QString& title) 
{ 
    QString result; 

    QString::const_iterator itr = title.begin();
    QString::const_iterator end = title.end();

    while (itr != end)
    {
        if (!itr->isHighSurrogate())
        {
            if (itr->isLetterOrNumber())
            {
                result.push_back(itr->toLower()); 
                ++itr;
                continue;
            }
        }
        else
        {
            ++itr;
            if (itr == end)
                break; // error - missing low surrogate

            if (!itr->isLowSurrogate())
                break; // error - not a low surrogate

            /*
            letters/numbers should not need to be surrogated,
            but if you want to check for that then you can use
            QChar::surrogateToUcs4() and QChar::category() to
            check if the surrogate pair represents a Unicode
            letter/number codepoint...

            uint ch = QChar::surrogateToUcs4(*(itr-1), *itr);
            QChar::Category cat = QChar::category(ch);
            if (
                ((cat >= QChar::Number_DecimalDigit) && (cat <= QChar::Number_Other)) ||
                ((cat >= QChar::Letter_Uppercase) && (cat <= QChar::Letter_Other))
                )
            {
                result.push_back(QChar(ch).toLower()); 
                ++itr;
                continue;
            }
            */
        }

        result.push_back('_');
        ++itr; 
    }

    return result; 
} 

【讨论】:

  • 感谢您提供完整代码。我试过了,但没有用(我把注释的部分去掉了,但这不应该是原因): § 仍然像上面一样输出。你知道为什么吗?在应用你的代码之前我需要打电话给setCodecForCStrings吗?
  • 我通过调试发现if (!itr-&gt;isHighSurrogate()) 被输入了两次,所以两者似乎都是低代理。 oO
  • 那么QString 格式错误,因为§ 在UTF-16 中没有使用代理对,所以字符串中不应有2 个QChar 元素开头。跨度>
  • 我同意@ChristianStieber。您的输入未正确解析,因此产生了错误的QString。没有编译器会在 UTF-8 中存储 char* 文字,因此您需要使用编译器的实际编码,或者以您自己选择的编码手动编码字符,然后在生成 QString 时使用该编码.
  • 或者更好,使用L"§"QString::fromStdWString()
【解决方案2】:

好的,这是我的理论:当您将“§”文字提供给 QString 时,Qt 使用一些默认编码,因为您没有设置一个。如果您的编译器使用 UTF-8 存储字符串文字,您可能会输入 2 个字节,这些字节将转换为 2 个字符而不是 1 个字符。同样,您的“toAscii”输出也很可能做错事。

从外观上看,您必须找出编译器用于存储字符串文字的内容,并使用正确的值调用 setCodecForCStrings

编辑:根据您的描述,如果我不知道编译器的编码,我可能会先尝试 QTextCodec::codecForName("UTF-8") 作为 setCodec 的参数 :-)

【讨论】:

  • 我在QTextCodec::setCodecForCStrings ( QTextCodec::codecForName("UTF-1") );这行前面加上了,但没有改变其他任何东西。然而,它并没有改变我的makeFilename 函数。这个问题的其他答案也失败了。 :(
猜你喜欢
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多