среда, 5 сентября 2012 г.

Формирование QString (Qt) из std::string содержащих национальные символы

Как сделать QString (Qt) из std::string с русскими буквами

Занимался я сегодня написанием некоторого демонстрационно-тестового приложения с использованием Qt. Приложение это должно выполнять роль GUI-обертки над объектами некой специализированной библиотеки, которую я написал в boost для некоторой прикладной области.

Работа выполняется в Linux. Дистрибутив Ubuntu 11.10. Локаль - UTF-8.

И вот возникла у меня проблема. Средствами boost::system::error_code, в ядре моей библиотеки формировалось некоторое локализованное сообщение (т.е. на русском языке), которое мне понадобилось отобразить средствами Qt в экземпляре класса QTextEdit.

Вообще, надо бы давно запретить использование национальных языков в системных сообщениях. Однако, видимо, это кому-то кажется недемократичным, поэтому программисты ежедневно тысячами подрываются на этих проблемах и мы постоянно в готовых приложениях сталкиваемся с кракозябрами там, где могли бы прочитать нормальный английский текст. Ведь в чем проблема? Домохозяйки не удосуживаются читать даже сообщения прикладного уровня, и представить домохозяйку, которая поймет, что надо делать прочитав системное сообщение из сетевой подсистемы ядра типа "Адрес уже используется" на родном языке, мне кажется невероятным. А раз это не для домохозяйки, то зачем усложнять жизнь специалистам, которые все поголовно умеют читать по английски? К этому еще можно добавить проблему некорректных переводов. Понятно, вопрос риторический. Однако, хочется высказаться.

На самом деле проблемы я не понял и был крайне удивлен, когда преобразовав полученное сообщение из std::string в QString и выведя его потом в окно редактора я увидел кракозябры. Действительно. В чем может быть проблема? Если бы я, не дай бог, работал под Windows, то удивляться бы было не чему. Давно не интересовался как там дела сейчас, но во времена Windows XP меня веселило три одновременно используемые кодовые страницы в русских версиях операционной системы. Вот уж действительно, не операционная система а система заплаток. Но откуда могла взяться проблема в Linux? Если в ней, в используемой мной сборке, используется одна кодовая страница UTF-8 во всех подсистемах ядра и пользовательского пространства.

Проблема решется просто. Приведу ниже максимально подробный вариант решения с комментариями.

  QTextCodec *codec = QTextCodec::codecForName("UTF8");
  if (codec)
  {
    std::string str = boost_lib->getLastError(); // Get string from a boost library
    QByteArray ba(str.c_str());                  // Convert to QByteArray
    QString msg = codec->toUnicode(ba);          // Qt magic !!! 

    m_pteLog->append(msg);                       // Append msg to a QTextEdit object
  }

В общем, заплатку я нашел, но в проблеме разбираться не стал. Видимо, по умолчанию, текст получаемый в Qt из строковых однобайтовых типов воспринимаются в кодировке Latin1, поэтому и возникала, в моем случае, неприятная неправильная перекодировка.

В заключении этой короткой заметки я бы хотел выразить надежду, что если кто-нибудь из читателей знает подробности затрагиваемого здесь механизма трансляции кодов символов, то пусть он его объяснит в комментариях или письмом мне, а я сделаю дополнение.

5 комментариев:

Игорь комментирует...

Алексей, вспомните про волшебный setCodecForCStrings и будет вам счастье без всяких явных дополнительных преобразований :)

пример:

#include
#include

int main(int argc, char **argv)
{
QApplication app(argc, argv);
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF8"));

std::string str("Пивет, мир!");
QString qstr(str.c_str());

QLabel lb(qstr);
lb.show();

return app.exec();
}

knzsoft комментирует...

Игорь, чтобы вспомнить нужно знать прежде :) Я до сих пор не найду время одолеть эту часть Qt. Спасибо тебе за ценное дополнение по теме.

TopSergey комментирует...

Осторожнее с функцией "setCodecForCStrings".
Решение, без сомнений элегантное.
Но, к примеру, в бетте qt5 я этой функции просто не обнаружил. (((

Sealkeen комментирует...

В QString символы хранятся в массиве и имеют тип unsigned short, что представляет из себя unicode. std::string представляет из себя строку 8-ми байтовых char'ов.

knzsoft комментирует...

для Sealkeen
Я бы уточнил. Во-первых, вы, видимо, опечатались - не 8-ми байтовые, а 8-ми битовые char-ы. Во-вторых, Unicode - это стандарт кодирования символов с максимальным кодом символа 0x10FFFF.

Для символов Unicode существует множество форм представления, включая UTF-8, UTF-16, UTF-32 и прочие. В каких особенностях представления хранятся данные в QString, при выделении двух байт (unsigned short) на символ, наверное, исследовали немногие, но, независимо от этого, никто не запретит вам трансформировать эти данные в цепочку байт для std::string. Да, произвольный посимвольный доступ в таком представлении будет невозможен, но побайтовое сканирование с последовательной расшифровкой байт в символы сделать можно.