Файл Qt UTF-8 в std::string Добавляет дополнительные символы

У меня есть текстовый файл в кодировке UTF-8, в котором есть такие символы, как ², ³, Ç и ó. Когда я читаю файл, используя приведенное ниже, кажется, что файл читается правильно (по крайней мере, в соответствии с тем, что я вижу в редакторе Visual Studio при просмотре содержимого переменной contents)

QFile file( filePath );
if ( !file.open( QFile::ReadOnly | QFile::Text ) ) {
    return;
}
QString contents;
QTextStream stream( &file );
contents.append( stream.readAll() );
file.close();

Однако, как только содержимое преобразуется в std::string, добавляются дополнительные символы. Например, ² преобразуется в ², хотя должно быть просто ². Похоже, это происходит для каждого символа, отличного от ANSI, добавляется дополнительный Â, что, конечно, означает, что при сохранении нового файла символы в выходном файле неверны.

Я, конечно, пытался просто сделать toStdString(), я также пробовал toUtf8 и даже пытался использовать QTextCodec, но каждый из них не дает правильных значений.

Я не понимаю, почему переход от файла UTF-8 к QString, а затем к std::string теряет символы UTF-8. Он должен воспроизводить именно тот файл, который был прочитан изначально, или я что-то совсем упустил?


person ChrisMM    schedule 11.07.2019    source источник
comment
Лишний символ в самой строке? Вы проверяли с помощью отладчика? Или вы видите это только при печати (в этом случае это может быть печать с ошибками, либо код, либо установленная локаль, либо терминал, на который вы печатаете). Короче говоря, не могли бы вы рассказать нам, как вы проверяете содержимое строки.   -  person Some programmer dude    schedule 11.07.2019
comment
При использовании TextVisualizer для QString я вижу ² (как и ожидалось), при просмотре TextVisualizer для std::string (сразу после вызова toStdString) я вижу ², так что это до того, как файл будет записан. Я также могу просматривать файлы (до и после) в Notepad ++ в ANSI (который показывает указанные выше символы) и в UTF-8, который показывает ² для исходного файла и ² для нового файла. Похоже, что версия UTF-8 показывает, какой она должна быть для ANSI.   -  person ChrisMM    schedule 11.07.2019
comment
Пожалуйста, не используйте ANSI, говоря о кодировках символов, потому что кодировки с таким именем не существует. Это просто неправильное название, которое Windows использует для обозначения кодовой страницы в соответствии с текущими региональными настройками, что приводит к тому, что текстовые файлы ANSI почти всегда не переносятся между версиями Windows на разных языках.   -  person Daniel Kamil Kozar    schedule 11.07.2019
comment
КСТАТИ. Пожалуйста, опубликуйте фактические шестнадцатеричные дампы данных, которые вы обрабатываете, и как они выглядят после прохождения через QString и std::string. Кроме того, похоже, что QTextStream готов обрабатывать UTF-16 по умолчанию, если только не появляется спецификация: вы пытались прочитать файл в QByteArray и использовать QString::fromUtf8? Это также имеет дополнительное преимущество, заключающееся в том, что вы четко указываете, что вы делаете с точки зрения кодировки символов.   -  person Daniel Kamil Kozar    schedule 11.07.2019


Ответы (2)


Как упомянул в своем ответе Даниэль Камил Козар, QTextStream не читается в кодировке и, следовательно, на самом деле не читает файл правильно. QTextStream должен установить свой кодек перед чтением файла, чтобы правильно анализировать символы. Добавлен комментарий к коду ниже, чтобы показать необходимый дополнительный файл.

QFile file( filePath );
if ( !file.open( QFile::ReadOnly | QFile::Text ) ) {
    return;
}
QString contents;
QTextStream stream( &file );
stream.setCodec( QTextCodec::codecForName( "UTF-8" ) ); // This is required.
contents.append( stream.readAll() );
file.close();
person ChrisMM    schedule 12.07.2019

То, что вы видите, на самом деле является ожидаемым поведением.

Строка ² состоит из байтов C3 82 C2 B2 в кодировке UTF-8. Предполагая, что QTextStream действительно правильно распознает UTF-8 (что не так уж очевидно, судя по документации, в которой обнаружение кодировки символов упоминается только при наличии спецификации, и вы ничего не сказали о входном файле, имеющем спецификацию), мы можем предположить, что QString, возвращаемый QTextStream::readAll, на самом деле содержит строка ².

QString::toStdString() возвращает вариант строки в кодировке UTF-8, QString, поэтому возвращаемое значение должно содержать те же байты, что и входной файл, а именно C3 82 C2 B2.

Теперь о том, что вы видите в отладчике:

  1. В одном из комментариев вы указали, что «QString имеет только 0xC2 0xB2 в строке (что правильно)». Это верно лишь отчасти: QString внутри использует UTF-16LE, что означает, что его внутренний массив символов содержит два 16-битных значения: 0x00C2 0x00B2. Фактически они сопоставляются с символами Â и ², когда каждый из них кодируется как UTF-16, что доказывает, что QString построен правильно на основе ввода из файла. Однако ваш отладчик, кажется, достаточно умен, чтобы знать, что байты, составляющие QString, закодированы в UTF-16 и, таким образом, правильно отображают символы.
  2. Вы также заявили, что отладчик показывает содержимое std::string, возвращенное из QString::toStdString, как ². Предполагая, что ваш отладчик использует страшную «кодовую страницу ANSI» для преобразования байтов в символы, когда кодировка не указана явно, и вы используете англоязычную Windows, которая использует Windows-1252 в качестве устаревшей кодовой страницы по умолчанию, все встает на свои места. : std::string на самом деле содержит байты C3 82 C2 B2, которые сопоставляются с символами ² в Windows-1252< /а>.

Бесстыдная самостоятельная вилка : я выступал с докладом о кодировках символов на конференции в прошлом году. Возможно, его просмотр поможет вам лучше понять некоторые из этих проблем.

И последнее: ANSI — это не кодировка. Это может означать несколько различных кодировок в зависимости от региональных настроек Windows.

person Daniel Kamil Kozar    schedule 11.07.2019
comment
Привет, Даниэль, после просмотра дампа памяти QString кажется, что QTextStream читает символ UTF-8 как два символа ASCII. Файл не имеет спецификации, так как не рекомендуется для файлов UTF-8. Для QTextStream необходимо установить кодировку перед чтением файла. - person ChrisMM; 12.07.2019
comment
(вздох) Они не могут быть символами ASCII... неважно. - person Daniel Kamil Kozar; 12.07.2019
comment
Даниил, можешь объяснить, что ты имеешь в виду? Может быть, я просто использовал неправильное слово? QTextStream интерпретировал ² как два отдельных символа (не ASCII)? - person ChrisMM; 12.07.2019