2011-01-22 21 views
12

Pasé todo un día tratando de resolver esto sin suerte. Busqué Everywhere pero no tuve suerte con el código de trabajo.Consola de Windows y Qt Texto Unicode

SO: Win XP Sp2 IDE & MARCO: C++, Qt Creator 2.0.

Estoy tratando de dar salida a un texto unicode (UTF-8) a la consola de Windows, pero todo lo que veo es un galimatías en lugar de los caracteres Unicode. Sé que la consola win admite unicode (desde win 2000) ... al menos según Wikipedia y muchos en la red, pero no veo cómo hacer que funcione con Qt. La mayoría de las "soluciones" que he visto (no he visto muchas) usan tecnología C++ y WInAPI ... que no puedo usar porque esa no es la manera Qt. Estoy usando QStrings y Qt!

El código está abajo. Saqué todas las cosas diferentes que traté de mantener el código simple para la publicación. Espero que alguien pueda hacer que el código funcione.

#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(); 
} 
+2

Unicode solo funciona si configura una fuente adecuada, generalmente no funciona, ya que se predetermina a una página ANSI. Consulte las preguntas relacionadas para intentos similares: http://stackoverflow.com/questions/2849010/output-unicode-to-console-using-c –

+0

... Al parecer, puede establecer cosas como la codificación de la consola en QT ... intentado lo que pude en ese sentido, pero todos los intentos fallaron. Espero que alguien sepa cómo usar QT/unicode/console. – user440297

+2

Creo que deberás crear una subclase personalizada QIODevice (o QTextStream) utilizando las soluciones que no sean Qt que hayas encontrado. (Tenga en cuenta que es Qt, no QT, que es Apple QuickTime). –

Respuesta

9

Bien, hice algunas pruebas con este código. No se requiere una configuración especial para la consola.

#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; 
} 

En Windows imprime cuadros cuadrados para todos los textos excepto ruso y europeo. Parece que Lucida Console no tiene soporte para hebreo y japonés. Lo curioso es que cuando copio el texto de la consola al portapapeles y luego pego en alguna parte con soporte Unicode (por ejemplo, en un navegador), aparece correctamente. Esto prueba que Windows realmente genera Unicode, simplemente no lo muestra. Se necesita alguna fuente de consola con soporte completo de Unicode.

Tenga en cuenta que en el ejemplo anterior he anulado único operator<<(), pero que tendría que reemplazar todos ellos si quería utilizar, porque se vuelven QTextStream& pero no son virtuales, por lo que es necesario que se den devuelva ConsoleTextStream&; de lo contrario, algo como qout << 1 << someUnicodeString no funcionará correctamente.

También probé este ejemplo en Linux con configuración regional UTF-8, funciona perfectamente.

Entrada de consola con ReadConsoleW() funciona porque la consola está configurada en el modo de entrada de línea por defecto, por lo que espera hasta que el usuario presione Enter pero no espera hasta que haya suficientes caracteres disponibles para llenar el búfer, por lo que hace exactamente lo que queremos: lee una línea siempre que el tamaño del búfer sea suficiente.

2

creo que es debido a que su código debe utilizar en lugar de WriteConsoleWWriteFile internamente, y la biblioteca de tiempo de ejecución podría no usar esa función. Si no usa WriteFileW, entonces no puede escribir Unicode.

4

Está cometiendo errores en ambas fases: entrada y salida.

entrada

No se puede escribir
QString szqLine = QString::fromUtf8("abc áéüóöú őű");
y esperamos tener una cadena Unicode válida como el resultado, porque esto no está garantizado por el Estándar de C++ (ver a cuestionar C++ source in unicode para más detalles).

Puede comprobar que no tiene una cadena Unicode válida utilizando código como este

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

Si szqLine eran una cadena Unicode válida que se obtiene una lista de puntos de código Unicode de caracteres de la cadena. En el caso de su cadena no obtiene salida.

La forma correcta de hacerlo es como esto

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

Ver QString::QString (const QChar * unicode, int size), QChar::QChar (int code) funciones QT y Full UTF-8 Character Map para Unicode puntos de código de sus personajes.

salida

consola de Windows utiliza una página de códigos específica para la entrada y otra de salida (ver Console Code Pages) cuando se utiliza mecanismos de entrada/salida estándar. Esto restringe el conjunto de caracteres que puede ingresar y mostrar a estos presentes en la página de códigos actual. Sin embargo, puede utilizar WriteConsole Win API function para generar cualquier cadena Unicode codificada en UTF-16. No hay manera de que pueda evitar el uso de la función Win API aquí porque no hay Qt API que pueda usarse aquí. A continuación se muestra un ejemplo completo que muestra cómo mostrar los caracteres de su pregunta en la consola de 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(); 
} 
+0

Gracias Piotr, no estoy seguro de que tenga razón acerca de la invalidez de QString szqLine = QString :: fromUtf8 ("abc áéüóöú őű"); En un experimento anterior hice una prueba similar a lo que propones y [CODE SAMPLE] foreach (QChar cqCharacter, szqLine22) { fuera << cqCharacter << endl; } [/ CODE SAMPLE] funcionó bien. Pone cada QChar perfectamente en un archivo en su propia línea. También todos los métodos de QString como .length() etc. dan el resultado correcto. – user440297

+1

@ user440297 Ok, pero es complicado. Yo no haría esto. –

0

Hoy he llegado un poco más lejos con el siguiente código.Ahora muestra unicode correctamente en la consola, pero aún no funciona del todo bien porque la consola se congela/se cuelga después de que el primer texto Unicode se coloca en la consola y no se muestra nada posterior a eso en la consola. Es como si los caracteres Unicode causaran que el buffer de la consola se confundiera después de la primera salida de texto.

#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(); 
} 
+0

@ user440297 Como no uso la codificación UTF-8 para mis archivos fuente, cambié el código para usar mi método de creación de QString. Cuando ejecuto este código, obtengo los caracteres correctos impresos pero, además, obtengo algo de basura después de ellos y el mensaje 'Escribe algunos caracteres unicode como los de arriba:' en la parte inferior. El programa no falla y la consola no se congela; Puedo ingresar caracteres y luego de presionar Enter se muestran. –

+0

@Piotr, probé el método de inicialización QString codificado utilizando puntos de código ASCII Unicode. Sin WriteConsoleW no hay beneficio ... exactamente el mismo efecto. La consola muestra la primera salida Unicode correctamente y luego se congela. Creo que no se congela porque usas el WinAPI para enviar la cadena a la consola. Tengo la sensación de que tiene que ver con que la BOM no es correcta cuando se usa QTextStream en lugar de WriteConsoleW (WinAPI) para enviar a stdout. No estoy seguro de cómo solucionar eso. No quiero usar WinAPI porque eso excluye a Linux y MAC. – user440297

+0

@ user440297 En cuanto a excluir Linux y Mac; no es así Debes usar lo que funciona para cada sistema usando #IFDEFs. –

Cuestiones relacionadas