qDebug不打印包含二进制数据的完整QByteArray

时间:2012-06-06 11:18:55

标签: c++ qt qbytearray qdebug

我有一个QByteArray来存储从GPS接收的数据,这是部分二进制和部分ASCII。我想知道调试提案知道收到了什么,所以我写的是这样的qDebug

//QByteArray buffer;
//...
qDebug() << "GNSS msg (" << buffer.size() << "): " << buffer;

我在控制台收到这样的消息:

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...

但突然间我得到了一个新的打印迭代。数据尚未删除,已附加。所以新的消息大小例如3204,明显比之前的打印大。但它打印完全相同(但括号之间的新尺寸3204)。没有打印新数据,与上一条消息相同:

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...

我猜qDebug会停止打印,因为它有限制,或者因为它达到终止字符或类似的东西,但我只是在猜测。

对此行为的任何帮助或解释?

1 个答案:

答案 0 :(得分:23)

解决方案/解决方法:

实际上,qDebug()的{​​{1}}输出会被QByteArray字符截断。这与QByteArray没有关系;你甚至不能使用qDebug()输出'\ 0'字符。有关解释,请参阅下文。

'\0'

输出:

QByteArray buffer;
buffer.append("hello");
buffer.append('\0');
buffer.append("world");

qDebug() << "GNSS msg (" << buffer.size() << "): " << buffer;

甚至忽略以下任何参数:

GNSS msg ( 11 ):  "hello

输出:

qDebug() << "hello" << '\0' << "world";

您可以在调试之前替换字节数组中的特殊字符来解决此“问题”:

hello

输出:

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()!

那么为什么会这样呢?为什么我不能使用qDebug()输出GNSS msg ( 11 ): "hello\0world"

让我们深入了解Qt内部代码,找出'\0'的作用。 以下代码片段来自Qt 4.8.0源代码。

执行qDebug()时调用此方法:

qDebug() << buffer

上面的inline QDebug &operator<<(const QByteArray & t) { stream->ts << '\"' << t << '\"'; return maybeSpace(); } 类型为stream->ts,可进行转化 将QTextStream改为QByteArray

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; } 被调用(d->putString(QString)的类型是文本流的内部私有类),在为常量宽度做一些填充后调用d领域。我跳过write(QString)的代码并直接跳转到putString(QString),其定义如下:

d->write(QString)

如您所见,inline void QTextStreamPrivate::write(const QString &data) { if (string) { string->append(data); } else { writeBuffer += data; if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) flushWriteBuffer(); } } 有一个缓冲区。此缓冲区的类型为QTextStreamPrivate。那么当缓冲区最终打印在终端上时会发生什么?为此,我们必须找出当QString语句完成并将缓冲区传递给消息处理程序时会发生什么,默认情况下,它会在终端上打印缓冲区。这发生在qDebug()类的析构函数中,其定义如下:

QDebug

所以这里是非二元安全的部分。 Qt采用文本缓冲区,将其转换为“本地8位”二进制表示(直到现在,AFAIK我们仍然应该有我们想要调试的二进制数据)。

但是然后将其传递给消息处理程序,而不另外指定二进制数据的长度。正如您应该知道的那样,无法找出C字符串的长度,该字符串也应该能够容纳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; } } 个字符。 (这就是为什么上面代码中的'\0'需要额外的二进制安全长度参数。)

因此,如果您想处理QString::fromAscii()字符,即使编写自己的消息处理程序也无法解决问题,因为您无法知道长度。悲伤,但却是真的。