Консоль Windows и текст Qt Unicode - PullRequest
12 голосов
/ 22 января 2011

Я провел целый день, пытаясь понять это без удачи.Я посмотрел везде, но не повезло с рабочим кодом.

ОС: Win XP Sp2 IDE & FRAMEWORK: C ++, Qt Creator 2.0.

Я пытаюсь вывести некоторый текст в кодировке Unicode (UTF-8)на консоли Windows, но все, что я вижу, это тарабарщина вместо символов Юникода.Я знаю, что консоль win поддерживает Unicode (начиная с win 2000) ... по крайней мере, согласно Википедии и многим в сети, но я не понимаю, как заставить ее работать с Qt.Большинство "решений", которые я видел (не видел многих), используют технологию C ++ и WInAPI ... которую я не могу использовать, потому что это не так, как в Qt.Я использую QStrings и Qt!

Код ниже.Я вынул все разные вещи, которые старался сделать код простым для поста.Надеюсь, кто-нибудь сможет заставить код работать.

#include <QtCore/QCoreApplication>
#include <QString>
#include <QTextStream>          
#include <QDate>
#include <QFile>
using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QTextStream qin(stdin);         
    QTextStream qout(stdout);       

    //The last 2 chars in QString each need a double slash for an accent.
    QString szqLine = QString::fromUtf8("abc áéüóöú őű");

    //I want this text console output to be in red text color.
    qout << "Bellow are some unicode characters: " << endl; 

    //The Win XP console does not display the unicode chars correctly!!
    //The cosole does not display unicode chars even though it is capable
    //according to wikipedia.  I just don't know how with Qt.
    //I want this text output in white(or default font color, not red.)
    qout << szqLine << endl;

    //Would be nice to get some unicode input from console too.
    qout << "Write some unicode chars like above: " << endl;
    QString szqInput;
    szqInput = QString::fromUtf8(qin.readLine());
    qout << "You wrote: " << endl;
    qout << szqInput << endl;



    return app.exec();
}

Ответы [ 4 ]

9 голосов
/ 23 января 2011

Хорошо, я провел некоторое тестирование с этим кодом.Никаких специальных настроек для консоли не требуется.

#include <QTextStream>

#ifdef Q_OS_WIN32
#include <windows.h>
#include <iostream>
#else
#include <locale.h>
#endif

class ConsoleTextStream: public QTextStream {
  public:
    ConsoleTextStream();
    ConsoleTextStream& operator<<(const QString &string);
};

ConsoleTextStream::ConsoleTextStream():
  QTextStream(stdout, QIODevice::WriteOnly)
{
}

ConsoleTextStream& ConsoleTextStream::operator<<(const QString &string)
{
#ifdef Q_OS_WIN32
  WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
      string.utf16(), string.size(), NULL, NULL);
#else
  QTextStream::operator<<(string);
#endif
  return *this;
}

class ConsoleInput: public QTextStream {
public:
  ConsoleInput();
  QString readLine();
};

ConsoleInput::ConsoleInput():
  QTextStream(stdin, QIODevice::ReadOnly)
{
}

QString ConsoleInput::readLine()
{
#ifdef Q_OS_WIN32
  const int bufsize = 512;
  wchar_t buf[bufsize];
  DWORD read;
  QString res;
  do {
    ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE),
        buf, bufsize, &read, NULL);
    res += QString::fromWCharArray(buf, read);
  } while (read > 0 && res[res.length() - 1] != '\n');
  // could just do res.truncate(res.length() - 2), but better be safe
  while (res.length() > 0 
         && (res[res.length() - 1] == '\r' || res[res.length() - 1] == '\n'))
    res.truncate(res.length() - 1);
  return res;
#else
  return QTextStream::readLine();
#endif
}

int main()
{
#ifndef Q_OS_WIN32
  setlocale(LC_ALL, "");
#endif
  ConsoleTextStream qout;
  qout << QString::fromUtf8("Текст на иврите: לחם גרוזיני מסורתי הנאפה בתנור לבנים\n");
  qout << QString::fromUtf8("Текст на японском: ※当サイト内コンテンツ・画像・写真データの、転載・転用・加工・無断複製は禁止いたします。\n");
  qout << QString::fromUtf8("Текст на европейском: áéüóöú őű\n");
  qout << flush; // needed on Linux
  ConsoleInput qin;
  QString s = qin.readLine();
  qout << s << endl;
  s = qin.readLine(); // one more time, to ensure we read everything ok
  qout << s << endl;
  return 0;
}

В Windows она печатает квадратные рамки для всего текста, кроме русского и европейского.Похоже, что Lucida Console не поддерживает иврит и японский.Самое смешное, что когда я копирую текст из консоли в буфер обмена, а затем вставляю куда-нибудь с поддержкой Юникода (например, в браузере), он отображается правильно.Это доказывает, что Windows фактически выводит Unicode, просто не отображает его.Необходим некоторый консольный шрифт с полной поддержкой Unicode.

Обратите внимание, что в приведенном выше примере я переопределил только один operator<<(), но мне нужно будет переопределить их все, если я хочу их использовать, потому что они возвращают QTextStream&, но не являются виртуальными, поэтому необходимо, чтобы все они возвращали ConsoleTextStream&, иначе что-то вроде qout << 1 << someUnicodeString не будет работать правильно.

Я также тестировал этот пример на Linux с UTF-8locale, работает отлично.

Консольный ввод с ReadConsoleW () работает, потому что консоль по умолчанию настроена в так называемом режиме линейного ввода, поэтому она ждет, пока пользователь нажмет Enter, но не дождется, пока будет доступно достаточно символов.заполнить буфер, чтобы он делал именно то, что нам нужно: читает строку при условии, что размера буфера достаточно.

4 голосов
/ 22 января 2011

Вы делаете ошибки в обеих фазах - входе и выходе.

Input

Вы не можете написать
QString szqLine = QString::fromUtf8("abc áéüóöú őű");
и надеемся получить в результате действительную строку Unicode, поскольку это не гарантируется стандартом C ++ (подробности см. в вопросе SO Источник C ++ в Unicode ).

Вы можете проверить, что у вас нет действительной строки Unicode, используя такой код

foreach(QChar ch, szqLine) {
  qout << ch.unicode();
}

Если бы szqLine была допустимой строкой Unicode, вы бы получили список кодовых точек Unicode символов в строке. В случае вашей строки вы не получите вывод.

Правильный способ сделать это так:

QChar const chars[] = { 'a', 'b', 'c', ' ', 255, 233, 252, 243, 246, 250, ' ', 337, 369};
QString s(&chars[0], sizeof(chars)/sizeof(QChar));

См. QString :: QString (const QChar * unicode, int size) , QChar :: QChar (int code) Функции Qt и Полная таблица символов UTF-8 для кодовых точек Unicode ваших персонажей.

выход

Консоль Windows использует одну конкретную кодовую страницу для ввода и другую для вывода (см. Кодовые страницы консоли ) при использовании стандартных механизмов ввода / вывода. Это ограничивает набор символов, которые вы можете вводить и отображать, чтобы они присутствовали в текущей кодовой странице. Однако вы можете использовать WriteConsole Win API для вывода любой строки Unicode, закодированной в UTF-16. Вы не можете избежать использования Win API здесь, потому что здесь нет Qt API. Ниже приведен полный пример, показывающий, как отображать символы из вашего вопроса на консоли Windows.

#include <QtCore/QCoreApplication>
#include <QString>
#include <QTextCodec>

#include <Windows.h>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QChar const chars[] = { 'a', 'b', 'c', ' ', 255, 233, 252, 243, 246, 250, ' ', 337, 369};                
    QString s(&chars[0], sizeof(chars)/sizeof(QChar));

    WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), s.utf16().constData(), s.size(), NULL, NULL);

    return app.exec();
}
2 голосов
/ 22 января 2011

Я думаю, это потому, что ваш код должен использовать WriteConsoleW вместо WriteFile внутри, и библиотека времени выполнения может не использовать эту функцию. Если он не использует WriteFileW, то вы не можете написать Unicode.

0 голосов
/ 22 января 2011

Я получил немного дальше с кодом ниже. Теперь он корректно отображает юникод на консоли, но все еще не работает должным образом, потому что консоль зависает / вылетает после того, как первый текст юникода выводится на консоль, и ничего после этого не отображается на консоли. Это похоже на то, что символы unicode приводят к путанице в консольном буфере после первого вывода текста.

#include <QtCore/QCoreApplication>
#include <QString>
#include <QTextStream>
using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QTextStream qin(stdin);
    qin.setCodec("UTF-8");
    qin.autoDetectUnicode();

    QTextStream qout(stdout);
    qout.setCodec("UTF-8");
    qout.autoDetectUnicode();

    //The last 2 chars in QString each need a double slash for an accent.
    QString szqLine = QString::fromUtf8("abc áéüóöú őű");

    qout << "Bellow are some unicode characters: " << endl;

    //Now this is displayed correctly on cosole but after displaying text
    //it no loger is capable of displaying anything subsequently.
    qout << szqLine << endl;

    //Would be nice to get some unicode input from console too.
    qout << "Write some unicode chars like above: " << endl;
    QString szqInput;
    szqInput = qin.readLine();
    qout << "You wrote: " << endl;
    qout << szqInput << endl;

    return app.exec();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...