How to print Unicode string using QTextStream? - c++

I want to print Unicode text into Windows console by using Qt (QTextStream). A code page of the console should be set to Unicode by using SetConsoleOutputCP(CP_UTF8) function. However, when I am using a plain way, then the output contains some garbage symbols.
#include <QCoreApplication>
#include <QString>
#include <QTextStream>
#include <QTextCodec>
#include <windows.h>
int main(int argc, char *argv[])
{
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
QCoreApplication a(argc, argv);
QTextStream stream(stdout);
stream.setCodec("UTF-8");
QString hello = "1 привет 2 привет 3 привет";
stream << hello << endl;
QString hello_en = "1 hello 2 hello 3 hello";
stream << hello_en << endl;
return a.exec();
}
As you can see, after the hello string was printed also garbage symbols were printed, which consisted of part of this string. But the hello_en string consisting only of ASCII symbols was printed correct.
At the same time, if the codec of QTextStream was set to a system locale of console (in my case it is IBM 866), then the garbage symbols were not printed.
#include <QCoreApplication>
#include <QString>
#include <QTextStream>
#include <QTextCodec>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTextStream stream(stdout);
stream.setCodec("IBM 866");
QString hello = "1 привет 2 привет 3 привет";
stream << hello << endl;
QString hello_en = "1 hello 2 hello 3 hello";
stream << hello_en << endl;
return a.exec();
}
As you see, non-ASCII symbols are printed correctly.
So, when I am trying to use a QTextStream for printing the Unicode string to a Unicode console, then some garbage symbols are appearing in the stdout. Previous questions on Stackoverflow are suggesting a solution without using QTextStream.
What am I doing wrong? Or am I using this correctly and this is a bug in Qt and I should report it to the Qt bug tracker?
UPDATE 1
This issue become on Qt 5.8.0 MinGW 5.3.0 32-bit on Windows 8.1 Pro 64-bit. But, when I tried to use Qt 5.8.0 MSVC2015 on my system, all working well. I am confused. What's wrong? But I can use MSVC2015, because this is not essential.

Related

Qt 5.7 \n behavior

In a Qt 5.7 Console Application:
#include <QCoreApplication>
#include <QtDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString s = "MDJ\nTest.\n";
qDebug() << "MDJ\nTest.\n";
qDebug() << s;
/* Prints:
MDJ
Test.
MDJ\nTest.\n
*/
return a.exec();
}
i.e. the \n works as expected in a direct debug print, but is treated as just two plain characters when debug printing a (supposedly identical content) variable.
I'm encountering similar problems in Qt 5.7 Widget Applications as well.
I've searched the documentation, stackoverflow, and Qt Centre and I've been unable to discover what I'm doing wrong.
Could somebody please point me to a solution for this?
The docs give you hints:
<< QString()
Normally, QDebug prints the string inside quotes and transforms non-printable characters to their Unicode values (\u1234).
To print non-printable characters without transformation, enable the noquote() functionality. Note that some QDebug backends might not be 8-bit clean.
vs.
<< const char*
Writes the '\0'-terminated string, s, to the stream and returns a reference to the stream. The string is never quoted nor transformed to the output, but note that some QDebug backends might not be 8-bit clean.
Solution: qDebug().noquote() << "some\nspecial\nchars\n\tincluded"

Seeking to end of std::cin returns -1 error and error bit

I'm trying to write from a Qt application to a newly created QProcess. The Qt documentation tells me to create a process and use the ::write() function to write to the standard input of the new process. It's not working so I tried some basic testing in a program with std::cin and stdin streams.
When I start an ordinary program in main() I do:
int result = fseek(stdin, 0, SEEK_END); // result is -1, indicating error
result = ftell(stdin); // result is 0
Using the C++ std::istream I do:
std::cin.seekg(0, std::ios::end); // Or std::ios_base::end
int result = std::cin.tellg(); // Returns -1
bool isgood = std::cin.good(); // Returns false
I'm really confused. First of all I would like to know if std::cin and stdin are both the same stream, that is the standard input? One is an std::istream object, the other is a FILE typedef, _iobuf on my Visual Studio.
I'm pretty sure that seeking to the end of a stream doesn't set an error bit or EOF bit, as people use this method to seek to the end, get the size of a file and then seek back to 0 without having to clear() the error flags.
The two lines fseek(stdin, 0, SEEK_END) and then ftell(stdin) I have been told can be used to check if the standard input is empty or not, yet fseek() return -1 error.
I thought it was a problem with my system but I tried an online C++ compiler and I get much the same results, the only difference between that the online compiler returns -1 for ftell(stdin) instead of my Visual Studio, which returns 0.
I haven't been able to write to the standard input from my qt application, however, I can redirect the stdin to a file on my hard drive, and that does work. Help would be appreciated.
I would try reading from standard input the Qt way, e.g.
#include <QApplication>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFile in;
in.open(stdin, QFile::ReadOnly);
QTextStream stream(&in);
QString line;
while(true)
{
line = stream.readLine();
if(!line.isEmpty())
{
QMessageBox::about(0, "stdin", line);
}
}
}
You can run this very simple widget application from another application, using QProcess:
#include <QCoreApplication>
#include <QProcess>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QProcess process;
process.start("/full/path/to/the/receiver/application");
if(process.waitForStarted())
{
process.write("Hello\n");
}
return a.exec();
}
For completeness sake, this would work as well, on the reader side:
#include <QApplication>
#include <QMessageBox>
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
std::string line;
while(true)
{
std::getline(std::cin, line);
QString data(line.c_str());
if(!data.isEmpty())
{
QMessageBox::about(0, "stdin", data);
}
}
}

Printing qByteArray through qDebug

#include <QCoreApplication>
#include <QByteArray>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray dataReceivedFromSerialPort;
dataReceivedFromSerialPort.push_back(0x0A);
dataReceivedFromSerialPort.push_back(0x0B);
dataReceivedFromSerialPort.push_back(0x0C);
dataReceivedFromSerialPort.push_back(0x0D);
dataReceivedFromSerialPort.push_back(0x0E);
dataReceivedFromSerialPort.push_back(0x0F);
dataReceivedFromSerialPort.push_back(0x07);
dataReceivedFromSerialPort.push_back(0x02);
dataReceivedFromSerialPort.push_back(0x01);
dataReceivedFromSerialPort.push_back(0x02);
qDebug() << "tostr: " << dataReceivedFromSerialPort.toStdString().c_str();
return a.exec();
}
The above does not print any value. It doesn't print anything beyond "tostr: ". If I store 0x0A in uchar and then push it in qByteArray then this problem disappears.
What can I do print it in its current form?
Because the bytes you give are, in many encodings, various control characters (newlines, carriage returns, etc.). Going through std::string and char* means the bytes will be sent as they are to the terminal, and thus displayed that way (either not at all, or as various types of whitespace).
You can try to instead do one of these, depending on what you want:
qDebug() << dataFromSerialPort; // prints "\n\x0B\f\r\x0E\x0F\x07\x02\x01\x02"
qDebug() << QString::fromLatin1(dataFromSerialPort); // prints "\n\u000B\f\r\u000E\u000F\u0007\u0002\u0001\u0002"
qDebug() << dataFromSerialPort.toHex(); // "0a0b0c0d0e0f07020102"
qDebug() << qPrintable(dataFromSerialPort); // same as toStdString().c_str(), but IMO more readable.
These print the bytes in various escape sequences (QString uses unicode, that's why you see \u instead of \x there), as a readable hexadecimal representation as well "as is".
QDebug does special formatting for many known types, like QString and QByteArray, that is why the first three examples above print with quotes and write out the escape sequences (it's for debugging after all). qPrintable, which works very similar to toStdString().c_str() returns a char*, which QDebug does not format in any special way, which is why you get whitespace as the output (this is the same behaviour as std::cout and friends).

how to read stdin to end in qt?

I have a qt-app which can be invoked with:
cat bla.bin | myapp
Whats the easiest way to read the entire input (stdin) into a QByteArray on Win,Mac and Linux?
I tired several things, but none of them seems to work (on windows):
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QByteArray content;
//---Test 1: hangs forever, reads 0
while(!std::cin.eof()) {
char arr[1024];
int s = std::cin.readsome(arr,sizeof(arr));
content.append(arr,s);
}
//---Test 2: Runs into timeout
QFile in;
if(!in.open(stdin,QFile::ReadOnly|QFile::Unbuffered)) {
qDebug() << in.errorString();
}
while (in.waitForReadyRead(1000)) {
content+=in.readAll();
}
in.close();
return app.exec();
}
Am I having a Event-Loop Problem or shouldn't it work without?
The primary problem of actually reading from stdin stems from using readsome. readsome is generally not used to read from files (including stdin). Readsome is generally used for binary data on asynchronous sources. Technically speaking eof doesn't get set with readsome. read is different in that regard as it will set eof accordingly. There is an SO question/answer here that may be of interest. If you are supporting Linux and Windows and reading stdin, you have to be aware that on Windows stdin isn't opened in binary mode (neither is stdout). On Windows you would have to use _setmode on stdin. One way to do this is with #ifdefs using Q_OS_WIN32. Using QFile doesn't resolve this issue.
In the code you are trying to create it doesn't appear you are interested in actually having an event loop. You can still use QT objects like QByteArray without an event loop. In your code you read data in from stdin (cin) and then you executed return app.exec(); which put your console application into a loop waiting for events. You didn't add any events to the QT Event queue prior to app.exec(); so effectively the only thing you can do is end your application with control-c. If no event loop is needed then code like this should suffice:
#include <QCoreApplication>
#include <iostream>
#ifdef Q_OS_WIN32
#include <fcntl.h>
#include <io.h>
#endif
int main()
{
QByteArray content;
#ifdef Q_OS_WIN32
_setmode(_fileno(stdin), _O_BINARY);
#endif
while(!std::cin.eof()) {
char arr[1024];
std::cin.read(arr,sizeof(arr));
int s = std::cin.gcount();
content.append(arr,s);
}
}
Notice how we used a QByteArray but didn't have a QCoreApplication app(argc, argv); and a call to app.exec();

Unicode Windows console application (WxDev-C++/minGW 4.6.1)

I'm trying to make simple multilingual Windows console app just for educational purposes. I'm using c++ lahguage with WxDev-C++/minGW 4.6.1 and I know this kind of question was asked like million times. I'v searched possibly entire internet and seen probably all forums, but nothing really helps.
Here's the sample working code:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
/* English version of Hello world */
wchar_t EN_helloWorld[] = L"Hello world!";
wcout << EN_helloWorld << endl;
cout << "\nPress the enter key to continue...";
cin.get();
return 0;
}
It works perfectly until I try put in some really wide character like "Ahoj světe!". The roblem is in "ě" which is '011B' in hexadecimal unicode. Compiler gives me this error: "Illegal byte sequence."
Not working code:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
/* Czech version of Hello world */
wchar_t CS_helloWorld[] = L"Ahoj světe!"; /* error: Illegal byte sequence */
wcout << CS_helloWorld << endl;
cout << "\nPress the enter key to continue...";
cin.get();
return 0;
}
I heard about things like #define UNICODE/_UNICODE, -municode or downloading wrappers for older minGW. I tried them but it doesn't work. May be I don't know how to use them properly. Anyway I need some help. In Visual studio it's simple task.
Big thanks for any response.
Apparently, using the standard output streams for UTF-16 does not work in MinGW.
I found that I could either use Windows API, or use UTF-8. See this other answer for code samples.
Here is an answer, not sure this will work for minGW.
Also there are some details specific to minGW here