Я хочу конвертировать 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()
,
Мои вопросы:
makeFilename
? Будет ли это также работать для китайцев?В дополнение к тому, что сказали другие, имейте в виду, что QString
является строкой в кодировке UTF-16 Символ Юникод, который находится за пределами BMP, требует 2 QChar
значения, работающие вместе, называются суррогатной парой, чтобы закодировать этот символ. Документация QString говорит так же:
Символы Unicode со значениями кода выше 65535 хранятся с использованием суррогатных пар, то есть двух последовательных символов 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;
}
Хорошо, вот моя теория: когда вы передаете литерал «§» в QString, Qt использует некоторую кодировку по умолчанию, потому что вы ее не установили. Если ваш компилятор использует UTF-8 для хранения строковых литералов, вы можете передать ему 2 байта, которые преобразуются в 2 символа вместо одного. Аналогично, ваш вывод «toAscii», скорее всего, тоже делает не так.
Судя по всему, вам нужно выяснить, что ваш компилятор использует для хранения строковых литералов, и вызвать setCodecForCStrings с правильным значением.
РЕДАКТИРОВАТЬ: учитывая ваше описание, если бы я не знал кодировку для моего компилятора, я бы, вероятно, сначала попробовал QTextCodec :: codecForName («UTF-8») в качестве параметра для setCodec 🙂