QChar::isLetterOrNumber() завершается с ошибкой
Вопрос
Я хочу преобразовать 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 QChar для «§»?(решено)
- У вас есть решение для
makeFilename
?Будет ли это работать и для китайцев?
Решение
В дополнение к тому, что сказали другие, имейте в виду, что QString
представляет собой строку в кодировке UTF-16.Для символа Юникода, находящегося за пределами BMP, требуется 2 QChar
значения, работающие вместе, называемые суррогатной парой, для кодирования этого символа.А Документация по QString говорит так:
Символы Юникода со значениями кодов выше 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», скорее всего, тоже не делает неправильные вещи.
Из внешнего вида, вам придется выяснить, что ваш компилятор использует для хранения строковых литералов, и вызов
Редактировать: Учитывая ваше описание, если я не знал кодировку для моего компилятора, я бы, вероятно, попробую QtextCodec :: CodeCForname («UTF-8») в качестве параметра на SetCodec First: -)