Подтвердить что ты не робот

Консоль Windows и текст в формате Unicode Qt

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

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

Я пытаюсь вывести текст unicode (UTF-8) в консоль Windows, но все, что я вижу, это тарабарщина вместо символов unicode. Я знаю, что консоль win поддерживает unicode (начиная с win 2000)... по крайней мере, согласно Wikipedia и многим в сети, но я не вижу, как заставить ее работать с Qt. Большинство "решений", которые я видел (не видели много), используют технологии С++ и 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();
}
4b9b3361

Ответ 1

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

#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 не поддерживает иврит и японский язык. Самое смешное, когда я копирую текст с консоли в буфер обмена, а затем вставляю где-нибудь поддержку Unicode (например, в браузер), она отображается правильно. Это доказывает, что Windows фактически выводит Unicode, просто не отображает его. Необходим некоторый консольный шрифт с полной поддержкой Unicode.

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

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

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

Ответ 2

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

Ввод

Вы не можете писать
QString szqLine = QString::fromUtf8("abc áéüóöú őű");
и надеемся, что в результате получится допустимая строка Unicode, потому что это не гарантируется стандартом С++ (см. вопрос SO < источник С++ в формате 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();
}

Ответ 3

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

Ответ 4

Я получил немного дальше сегодня с кодом ниже. Теперь он корректно отображает юникод на консоль, но все еще не работает правильно, потому что консоль зависает/падает после того, как первый текст юникода выведен на консоль, и ничто после этого не отображается на консоли. Это похоже на то, что символы 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();
}