qDebug isn't printing a full QByteArray containing binary data - c++

I have a QByteArray to store data received from a GPS, which is part binary and part ASCII. I want to know for debug proposals know what's being received, so I'm writing a qDebug like this:
//QByteArray buffer;
//...
qDebug() << "GNSS msg (" << buffer.size() << "): " << buffer;
And I get messages like this at console:
GNSS msg ( 1774 ): "ygnnsdgk...(many data)..PR085hlHJGOLH
(more data into a new line, which is OK because it is a new GNSS sentence and
probably has a \n at the end of each one) blablabla...
But suddenly I get a new print iteration. Data has not been erased yet, it has been appended. So new message size its for example 3204, bigger than the previous print obviously. But it prints exactly the same (but with the new size 3204 between brackets). No new data is printed, just the same as the previous message had:
GNSS msg ( 3204 ): "ygnnsdgk...(many data)..PR085hlHJGOLH
(more data into a new line, which is OK because it is a new GNSS sentence and
probably has a \n at the end of each one) blablabla...
I guess qDebug stops printing because it has a limit, or because it reaches a terminating character or something like that, but I'm only guessing.
Any help or explanation for this behaviour?

Solution / workaround:
Indeed, the qDebug() output of QByteArray gets truncated at a '\0' character. This doesn't have something to do with the QByteArray; you even can't ever output a '\0' character using qDebug(). For an explanation see below.
QByteArray buffer;
buffer.append("hello");
buffer.append('\0');
buffer.append("world");
qDebug() << "GNSS msg (" << buffer.size() << "): " << buffer;
Output:
GNSS msg ( 11 ): "hello
Even any following arguments are ignored:
qDebug() << "hello" << '\0' << "world";
Output:
hello
You can work around this "problem" by replacing the special characters in your byte array before debugging them:
QByteArray dbg = buffer; // create a copy to not alter the buffer itself
dbg.replace('\\', "\\\\"); // escape the backslash itself
dbg.replace('\0', "\\0"); // get rid of 0 characters
dbg.replace('"', "\\\""); // more special characters as you like
qDebug() << "GNSS msg (" << buffer.size() << "): " << dbg; // not dbg.size()!
Output:
GNSS msg ( 11 ): "hello\0world"
So why is this happening? Why can't I output a '\0' using qDebug()?
Let's dive into the Qt internal code to find out what qDebug() does.
The following code snippets are from the Qt 4.8.0 source code.
This method is called when you do qDebug() << buffer:
inline QDebug &operator<<(const QByteArray & t) {
stream->ts << '\"' << t << '\"'; return maybeSpace();
}
The stream->ts above is of type QTextStream, which converts
the QByteArray into a QString:
QTextStream &QTextStream::operator<<(const QByteArray &array)
{
Q_D(QTextStream);
CHECK_VALID_STREAM(*this);
// Here, Qt constructs a QString from the binary data. Until now,
// the '\0' and following data is still captured.
d->putString(QString::fromAscii(array.constData(), array.length()));
return *this;
}
As you can see, d->putString(QString) is called (the type of d is the internal private class of the text stream), which calls write(QString) after doing some padding for constant-width fields. I skip the code of putString(QString) and directly jump into d->write(QString), which is defined like this:
inline void QTextStreamPrivate::write(const QString &data)
{
if (string) {
string->append(data);
} else {
writeBuffer += data;
if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE)
flushWriteBuffer();
}
}
As you can see, the QTextStreamPrivate has a buffer. This buffer is of type QString. So what happens when the buffer is finally printed on the terminal? For this, we have to find out what happens when your qDebug() statement finishes and the buffer is passed to the message handler, which, per default, prints the buffer on the terminal. This is happening in the destructor of the QDebug class, which is defined as follows:
inline ~QDebug() {
if (!--stream->ref) {
if(stream->message_output) {
QT_TRY {
qt_message_output(stream->type, stream->buffer.toLocal8Bit().data());
} QT_CATCH(std::bad_alloc&) { /* We're out of memory - give up. */ }
}
delete stream;
}
}
So here is the non-binary-safe part. Qt takes the textual buffer, converts it to "local 8bit" binary representation (until now, AFAIK we should still have the binary data we want to debug).
But then passes it to the message handler without the additional specification of the length of the binary data. As you should know, it is impossible to find out the length of a C-string which should also be able to hold '\0' characters. (That's why QString::fromAscii() in the code above needs the additional length parameter for binary-safety.)
So if you want to handle the '\0' characters, even writing your own message handler will not solve the problem, as you can't know the length. Sad, but true.

Related

std::ostringstream not initialized with std::string [duplicate]

I'm making an OpenGL game in C++. I'm fairly inexperinced in C++ as opposed to other languages. Anyway, I create a stringstream with the "base" directory for some images. I then pass this stringstream as a function parameter to a constructor. The constructor appends an image file name, then attempts to load the resulting path. However...
D:\CodeBlocks Projects\SnakeRoid\bin\Debug\Texts\ <-- before appending the filename
Ship01.tgacks Projects\SnakeRoid\bin\Debug\Texts\ <-- After.
Obviously not correct! The result should be D:\CodeBlocks Projects\SnakeRoid\bin\Debug\Texts\Ship01.tga
The relevant parts of my code:
std::stringstream concat;
std::string txtFullPath = "Path here";
...
concat.str(""); //Reset value (because it was changed in ...)
concat << texFullPath; //Restore the base path
PS = new PlayerShip(&TexMan, concat); //Call the constructor
The constructor's code
PlayerShip::PlayerShip(TextureManager * TexMan, std::stringstream &path)
{
texId = 2;
std::cout << path.str(); //First path above
path << "Ship01.tga";
std::cout << path.str(); //Second - this is the messed up one
//Do more fun stuff
}
Anyone have any idea why its "overwriting" what's already in the stringstream?
why its "overwriting" what's already in the stringstream
Because output places characters at the "put pointer" position in the output buffer. A freshly-constructed stream has the put pointer set to zero (except for file output streams opened in append mode), thus your output overwrites the characters already in the buffer.
If you really need to append strings this way, you need to move the put pointer to the end of the buffer:
std::cout << p.str(); //First path above
std::stringstream path;
path.str(p.str());
path.seekp(0, std::ios_base::end); // <-- add this
path << "Ship01.tga";
std::cout << "Loading player ship from " << path.str();
EDIT: The question has been edited and the code after the edit works, because it no longer uses path.str(p.str()); to create the output buffer without using an output operation (and without advancing the put pointer): see ideone for differences.
In any case, strings themselves can be concatenated, which would make the code easier to follow:
std::string p = path.str() + "Ship01.tga";
std::cout << p;
Not to mention that for dealing with files and pathnames, we have boost.filesystem.

No longer unable to retrieve data from QIODevice after calling readAll(). Buffer flushed?

I've just noticed something when using QNetworkReply that I was unable to find the slightest hint in the Qt documentation for QIODevice::readAll() (which the QNetworkReply inherits this method from).
Here is what the documentation states:
Reads all remaining data from the device, and returns it as a byte
array.
This function has no way of reporting errors; returning an empty
QByteArray can mean either that no data was currently available for
reading, or that an error occurred.
Let's say I have the following connection:
connect(this->reply, &QIODevice::readyRead, this, &MyApp::readyReadRequest);
Ths readyReadRequest() slot looks like this:
void MyApp::readyReadRequest()
{
LOG(INFO) << "Received data from \"" << this->url.toString() << "\"";
LOG(INFO) << "Data contents:\n" << QString(this->reply->readAll());
this->bufferReply = this->reply->readAll();
}
The surprise came after I called this->bufferReply (which a QByteArray class member of MyApp). I passed it to a QXmlStreamReader and did:
while (!reader.atEnd())
{
LOG(DEBUG) << "Reading next XML element";
reader.readNext();
LOG(DEBUG) << reader.tokenString();
}
if (reader.hasError())
{
LOG(ERROR) << "Encountered error while parsing XML data:" << reader.errorString();
}
Imagine my surprise when I got the following output:
2017-10-17 16:12:18,591 DEBUG [default] [void MyApp::processReply()][...] Reading next XML element
2017-10-17 16:12:18,591 DEBUG [default] [void MyApp::processReply()] [...] Invalid
2017-10-17 16:12:18,591 ERROR [default] Encountered error while parsing XML data: Premature end of document
Through debugging I got that my bufferReply at this point is empty. I looked in the docs again but couldn't find anything that hints removing the data from the device (in my case the network reply) after reading it all.
Removing the line where I print the byte array or simply moving it after this->bufferReply = this->reply->readAll(); and then printing the contents of the class member fixed the issue:
void MyApp::readyReadRequest()
{
LOG(INFO) << "Received data from \"" << this->url.toString() << "\"";
this->bufferReply = this->reply->readAll();
LOG(INFO) << "Data contents:\n" << QString(this->bufferReply);
}
However I would like to know if I'm doing something wrong or is the documentation indeed incomplete.
Since readAll() doesn't report errors or that data is not available at the given point in time returning an empty byte array is the only thing that hints towards the fact that something didn't work as intended.
Yes. When you call QIODevice::readAll() 2 times, it is normal that the 2nd time you get nothing. Everything has been read, there is nothing more to be read.
This behavior is standard in IO read functions: each call to a read() function returns the next piece of data. Since readAll() reads to the end, further calls return nothing.
However, this does not necessarily means that the data has been flushed. For instance when you read a file, it just moves a "cursor" around and you can go back to the start of the file with QIODevice::seek(0). For QNetworkReply, I'd guess that the data is just discarded.

Reading a string from a file in C++

I'm trying to store strings directly into a file to be read later in C++ (basically for the full scope I'm trying to store an object array with string variables in a file, and those string variables will be read through something like object[0].string). However, everytime I try to read the string variables the system gives me a jumbled up error. The following codes are a basic part of what I'm trying.
#include <iostream>
#include <fstream>
using namespace std;
/*
//this is run first to create the file and store the string
int main(){
string reed;
reed = "sees";
ofstream ofs("filrsee.txt", ios::out|ios::binary);
ofs.write(reinterpret_cast<char*>(&reed), sizeof(reed));
ofs.close();
}*/
//this is run after that to open the file and read the string
int main(){
string ghhh;
ifstream ifs("filrsee.txt", ios::in|ios::binary);
ifs.read(reinterpret_cast<char*>(&ghhh), sizeof(ghhh));
cout<<ghhh;
ifs.close();
return 0;
}
The second part is where things go haywire when I try to read it.
Sorry if it's been asked before, I've taken a look around for similar questions but most of them are a bit different from what I'm trying to do or I don't really understand what they're trying to do (still quite new to this).
What am I doing wrong?
You are reading from a file and trying to put the data in the string structure itself, overwriting it, which is plain wrong.
As it can be verified at http://www.cplusplus.com/reference/iostream/istream/read/ , the types you used were wrong, and you know it because you had to force the std::string into a char * using a reinterpret_cast.
C++ Hint: using a reinterpret_cast in C++ is (almost) always a sign you did something wrong.
Why is it so complicated to read a file?
A long time ago, reading a file was easy. In some Basic-like language, you used the function LOAD, and voilĂ !, you had your file.
So why can't we do it now?
Because you don't know what's in a file.
It could be a string.
It could be a serialized array of structs with raw data dumped from memory.
It could even be a live stream, that is, a file which is appended continuously (a log file, the stdin, whatever).
You could want to read the data word by word
... or line by line...
Or the file is so large it doesn't fit in a string, so you want to read it by parts.
etc..
The more generic solution is to read the file (thus, in C++, a fstream), byte per byte using the function get (see http://www.cplusplus.com/reference/iostream/istream/get/), and do yourself the operation to transform it into the type you expect, and stopping at EOF.
The std::isteam interface have all the functions you need to read the file in different ways (see http://www.cplusplus.com/reference/iostream/istream/), and even then, there is an additional non-member function for the std::string to read a file until a delimiter is found (usually "\n", but it could be anything, see http://www.cplusplus.com/reference/string/getline/)
But I want a "load" function for a std::string!!!
Ok, I get it.
We assume that what you put in the file is the content of a std::string, but keeping it compatible with a C-style string, that is, the \0 character marks the end of the string (if not, we would need to load the file until reaching the EOF).
And we assume you want the whole file content fully loaded once the function loadFile returns.
So, here's the loadFile function:
#include <iostream>
#include <fstream>
#include <string>
bool loadFile(const std::string & p_name, std::string & p_content)
{
// We create the file object, saying I want to read it
std::fstream file(p_name.c_str(), std::fstream::in) ;
// We verify if the file was successfully opened
if(file.is_open())
{
// We use the standard getline function to read the file into
// a std::string, stoping only at "\0"
std::getline(file, p_content, '\0') ;
// We return the success of the operation
return ! file.bad() ;
}
// The file was not successfully opened, so returning false
return false ;
}
If you are using a C++11 enabled compiler, you can add this overloaded function, which will cost you nothing (while in C++03, baring optimizations, it could have cost you a temporary object):
std::string loadFile(const std::string & p_name)
{
std::string content ;
loadFile(p_name, content) ;
return content ;
}
Now, for completeness' sake, I wrote the corresponding saveFile function:
bool saveFile(const std::string & p_name, const std::string & p_content)
{
std::fstream file(p_name.c_str(), std::fstream::out) ;
if(file.is_open())
{
file.write(p_content.c_str(), p_content.length()) ;
return ! file.bad() ;
}
return false ;
}
And here, the "main" I used to test those functions:
int main()
{
const std::string name(".//myFile.txt") ;
const std::string content("AAA BBB CCC\nDDD EEE FFF\n\n") ;
{
const bool success = saveFile(name, content) ;
std::cout << "saveFile(\"" << name << "\", \"" << content << "\")\n\n"
<< "result is: " << success << "\n" ;
}
{
std::string myContent ;
const bool success = loadFile(name, myContent) ;
std::cout << "loadFile(\"" << name << "\", \"" << content << "\")\n\n"
<< "result is: " << success << "\n"
<< "content is: [" << myContent << "]\n"
<< "content ok is: " << (myContent == content)<< "\n" ;
}
}
More?
If you want to do more than that, then you will need to explore the C++ IOStreams library API, at http://www.cplusplus.com/reference/iostream/
You can't use std::istream::read() to read into a std::string object. What you could do is to determine the size of the file, create a string of suitable size, and read the data into the string's character array:
std::string str;
std::ifstream file("whatever");
std::string::size_type size = determine_size_of(file);
str.resize(size);
file.read(&str[0], size);
The tricky bit is determining the size the string should have. Given that the character sequence may get translated while reading, e.g., because line end sequences are transformed, this pretty much amounts to reading the string in the general case. Thus, I would recommend against doing it this way. Instead, I would read the string using something like this:
std::string str;
std::ifstream file("whatever");
if (std::getline(file, str, '\0')) {
...
}
This works OK for text strings and is about as fast as it gets on most systems. If the file can contain null characters, e.g., because it contains binary data, this doesn't quite work. If this is the case, I'd use an intermediate std::ostringstream:
std::ostringstream out;
std::ifstream file("whatever");
out << file.rdbuf();
std::string str = out.str();
A string object is not a mere char array, the line
ifs.read(reinterpret_cast<char*>(&ghhh), sizeof(ghhh));
is probably the root of your problems.
try applying the following changes:
char[BUFF_LEN] ghhh;
....
ifs.read(ghhh, BUFF_LEN);

C++ - stringstream << "overwriting"

I'm making an OpenGL game in C++. I'm fairly inexperinced in C++ as opposed to other languages. Anyway, I create a stringstream with the "base" directory for some images. I then pass this stringstream as a function parameter to a constructor. The constructor appends an image file name, then attempts to load the resulting path. However...
D:\CodeBlocks Projects\SnakeRoid\bin\Debug\Texts\ <-- before appending the filename
Ship01.tgacks Projects\SnakeRoid\bin\Debug\Texts\ <-- After.
Obviously not correct! The result should be D:\CodeBlocks Projects\SnakeRoid\bin\Debug\Texts\Ship01.tga
The relevant parts of my code:
std::stringstream concat;
std::string txtFullPath = "Path here";
...
concat.str(""); //Reset value (because it was changed in ...)
concat << texFullPath; //Restore the base path
PS = new PlayerShip(&TexMan, concat); //Call the constructor
The constructor's code
PlayerShip::PlayerShip(TextureManager * TexMan, std::stringstream &path)
{
texId = 2;
std::cout << path.str(); //First path above
path << "Ship01.tga";
std::cout << path.str(); //Second - this is the messed up one
//Do more fun stuff
}
Anyone have any idea why its "overwriting" what's already in the stringstream?
why its "overwriting" what's already in the stringstream
Because output places characters at the "put pointer" position in the output buffer. A freshly-constructed stream has the put pointer set to zero (except for file output streams opened in append mode), thus your output overwrites the characters already in the buffer.
If you really need to append strings this way, you need to move the put pointer to the end of the buffer:
std::cout << p.str(); //First path above
std::stringstream path;
path.str(p.str());
path.seekp(0, std::ios_base::end); // <-- add this
path << "Ship01.tga";
std::cout << "Loading player ship from " << path.str();
EDIT: The question has been edited and the code after the edit works, because it no longer uses path.str(p.str()); to create the output buffer without using an output operation (and without advancing the put pointer): see ideone for differences.
In any case, strings themselves can be concatenated, which would make the code easier to follow:
std::string p = path.str() + "Ship01.tga";
std::cout << p;
Not to mention that for dealing with files and pathnames, we have boost.filesystem.

How to call qDebug without the appended spaces and newline?

I'm using the the C++/Qt print function qDebug,
but sometimes I would like to control how ", space and newline is appended
and not use the default qDebug.
Let's take a simple example:
QString var1("some string");
int var2 = 1;
qDebug() << var1 << "=" << var2;
This will print
"some string" = 1
But Let's say that I don't like the appended " and space
and would like the print to look like
some string=1
How to I then call qDebug?
Note: There is a function in qDebug called nospace, but it will remove the spaces.
But the " is still there.
If I use this:
qDebug().nospace() << var1 << "=" << var2;
I get:
"some string"=1
But please note that I have still not found a way to get rid of the ending newline.
/Thanks
It would be best to understand how QDebug works internally. That way you can easily modify it to suit your needs. Whenever you use the qDebug() function, it returns a QDebug object. By default QDebug always outputs a space after any use of operator <<.
The QDebug class internally contains a QString. Every time you use operator << you are appending to that internal QString. This QString is printed via qt_message_output(QtMsgType, char*) when the QDebug object is destroyed.
By default qt_message_output always prints the string followed by a newline.
Normal Output
qDebug() << "Var" << 1;
This will output Var 1. This is because qDebug will create a QDebug object which appends a space after each call to operator <<. So that will be Var + + 1 + .
Without Spaces
You can use QDebug::nospace to tell QDebug not to append a space after each call to operator <<.
qDebug().nospace() << "Var" << 1;
This will output Var1 as that QDebug object is no longer printing spaces.
Without New Lines
Not adding the \n at the end of the string is a little bit harder. Since QDebug internally only passes the string to qt_message_output when it is destroyed, you can delay the destruction of that QDebug object -
QDebug deb = qDebug();
deb << "One" << "Two";
deb << "Three";
This will print One Two Three and then append a new line.
If you never want a new line to be printed, you will have to change the behaviour of qt_message_output. This can be done by installing a custom handler.
void customHandler(QtMsgType type, const char* msg) {
fprintf(stderr, msg);
fflush(stderr);
}
// Somewhere in your program
qInstallMsgHandler(customHandler);
qDebug() << "One" << "Two";
qDebug().noSpace() << "Three" << "Four";
This will print One Two ThreeFour.
Be warned that this will affect all of the qDebug statements in your program. If you want to remove the custom handler, you should call qInstallMsgHandler(0).
qDebug(const char* msg, ...)
As indicated by the other answers you can also use the qDebug function to print strings in a format similar to that of printf. This way you can avoid the extra spaces that are appended by QDebug.
However, qDebug internally still uses qt_message_output, so you will still get a newline at the end unless you install your own handler.
Try this format: qDebug("%s=%d", "string", 1);
In this case qDebug uses printf formatting
P.S. Adapted for your example: qDebug("%s=%d", var1.toStdString().c_str(), var2);
Since Qt 5.4 you can also write:
qDebug().nospace().noquote() << var1;
Combining some of the above answers you can use
qDebug() << qPrintable(var1);
to eliminate the surrounding quotes.
I also experienced the quotes problem. The solution is to not pipe QString() into the stream but instead QString(...).toStdString().c_str().
I've built myself a small convenience macro to easily get around this:
#define Q(string) (string).toStdString().c_str()
Now everytime you use a QString, do it like that:
qDebug() << Q(var1) << "=" << var2;
The file $(QTDIR)/src/corelib/io/qdebug.h contains almost all definitions for the debug output methods. One of them is:
inline QDebug &operator<<(const QString & t) { stream->ts << '\"' << t << '\"'; return maybeSpace(); }
So there is no "official" way to suppress the quotes, but you can of course change qdebug.h or use your own copy or a modified and renamed copy of the QDebug class.
Another way is to use your own message handler.
Hope this helps.
Instantiate a QDebug object and output to it:
QDebug dbg = qDebug().nospace().noquote();
dbg << var1 << "=" << var2;
Yields:
some string=1
Output to the dbg object all you want -- there won't be a newline until it goes out of scope. For example:
char var1[] = "some string";
int var2 = 1;
{
QDebug dbg = qDebug().nospace().noquote();
dbg << var1 << "=" << var2;
// keep using "dbg"; there's no newline ('\n') until it destructs
dbg << "...";
for (int i = 5; i <=9; ++i)
dbg << i;
}
Outputs:
some string=1...56789