我有一个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
会停止打印,因为它有限制,或者因为它达到终止字符或类似的东西,但我只是在猜测。
对此行为的任何帮助或解释?
答案 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()
字符,即使编写自己的消息处理程序也无法解决问题,因为您无法知道长度。悲伤,但却是真的。