Windows控制台和Qt Unicode文本

时间:2011-01-22 05:14:10

标签: qt unicode console

我花了一整天的时间试图弄明白这一点,没有运气。我看着所有地方,但工作代码没有运气。

操作系统:Win XP Sp2 IDE& FRAMEWORK:C ++,Qt Creator 2.0。

我正在尝试将一些unicode(UTF-8)文本输出到Windows控制台,但我看到的只是乱码而不是unicode字符。我知道win控制台确实支持unicode(自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 个答案:

答案 0 :(得分:9)

好的,我用这段代码做了一些测试。无需为控制台进行特殊设置。

#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但不等到有足够的字符可用来填充缓冲区,所以它会正是我们想要的:只要缓冲区大小足够就读取一行。

答案 1 :(得分:4)

你在两个阶段都犯了错误 - 输入和输出。

<强>输入

你不能写
QString szqLine = QString::fromUtf8("abc áéüóöú őű");
并且希望有一个有效的Unicode字符串作为结果,因为C ++标准不保证这一点(详情参见SO问题C++ source in 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));

有关角色的Unicode代码点,请参阅QString::QString ( const QChar * unicode, int size )QChar::QChar ( int code ) Qt函数和Full UTF-8 Character Map

<强>输出

当您使用标准输入/输出机制时,Windows控制台使用一个特定代码页进行输入,另一个用于输出(请参阅Console Code Pages)。这会限制您可以输入并显示在当前代码页中的字符集。但是,您可以使用WriteConsole Win API函数输出以UTF-16编码的任何Unicode字符串。你无法避免在这里使用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 :(得分:2)

我认为这是因为您的代码需要在内部使用WriteConsoleW而不是WriteFile,并且运行时库可能不会使用该函数。如果它不使用WriteFileW,则无法编写Unicode。

答案 3 :(得分:0)

今天我用下面的代码得到了一点点。现在它正确地向控制台显示unicode,但仍然无法正常工作,因为控制台将第一个unicode文本输出到控制台后控制台冻结/崩溃,并且控制台上没有显示任何内容。就好像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();
}