【问题标题】:qDebug isn't printing a full QByteArray containing binary dataqDebug 没有打印包含二进制数据的完整 QByteArray
【发布时间】:2012-06-10 09:44:58
【问题描述】:

我有一个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 停止打印是因为它有一个限制,或者因为它到达了一个终止字符或类似的东西,但我只是猜测。

对这种行为有任何帮助或解释吗?

【问题讨论】:

    标签: c++ qt qbytearray qdebug


    【解决方案1】:

    解决方案/解决方法:

    确实,QByteArrayqDebug() 输出在 '\0' 字符处被截断。这与 QByteArray 无关;你甚至不能使用 qDebug() 输出一个 '\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()!
    

    输出:

    GNSS msg ( 11 ):  "hello\0world" 
    

    那么为什么会这样呢?为什么我不能使用 qDebug() 输出 '\0'

    让我们深入了解 Qt 内部代码以了解 qDebug() 的作用。 以下代码sn-ps来自Qt 4.8.0源代码。

    这个方法在你做qDebug() &lt;&lt; buffer时调用:

    inline QDebug &operator<<(const QByteArray & t) {
        stream->ts  << '\"' << t << '\"'; return maybeSpace();
    }
    

    上面的stream-&gt;tsQTextStream 类型,它转换 将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-&gt;putString(QString) 被调用(d 的类型是文本流的内部私有类),它在对等宽字段进行一些填充后调用write(QString)。我跳过putString(QString)的代码,直接跳转到d-&gt;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 类的析构函数中,其定义如下:

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

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

    但随后将其传递给消息处理程序无需额外指定二进制数据的长度。正如您应该知道的,不可能找出也应该能够容纳'\0' 字符的C 字符串的长度。 (这就是为什么上面代码中的QString::fromAscii() 需要额外的长度参数来保证二进制安全。)

    因此,如果您想处理'\0' 字符,即使编写自己的消息处理程序也无法解决问题,因为您无法知道长度。悲伤,但真实。

    【讨论】:

    • 我不会引用“问题”这个词,它看起来确实像一个错误(正如在 qDebug 通常添加的字符串之后缺少关闭 " 所证明的那样。
    • @fish 我还认为仅截断 '\0' 处的数据并不好。另一方面,我猜想 qDebug 输出是为文本数据制作的(至少对于 QString 和 QByteArray)。有人可能会争辩说,在 QByteArray 的情况下,这个 不应该 被假定,但也有人可能会争辩说 Qt 可能不知道您希望如何调试二进制数据。输出零字符有效,但无法读取(这是 qDebug() 的目的)。在某些情况下,也许 .toHex() 是二进制数据的更好解决方案。所以我猜 Qt 让你自己决定如何 输出数据。
    • 除此之外可以说这是一个正确的行为,我很确定这是无意的,否则它会附加缺少的“。”。
    • 是否也有可能在使用 QByteArray.indexOf("sample") 时,出于同样的原因,如果放在 \0 之后,则永远找不到字符串“sample”?
    • 没有。 QByteArray 是为二进制数据设计的,因此是“二进制安全的”(这意味着它平等地对待一个字节的所有 256 个可能值;没有一个像在 C 字符串中那样被特别对待,其中 0 表示结束)。只有qDebug 输出行为非二进制安全。即使是QDataStream 序列化,用于编码和解码 Qt 数据类型到/从二进制数据流(例如将其传输到另一个应用程序,或将其保存在文件中),也是二进制安全的。因此,也许 Qt 开发人员忘记关心二进制安全,或者这不是调试输出的设计要求。
    猜你喜欢
    • 2016-12-07
    • 2023-03-14
    • 2019-09-26
    • 2012-02-23
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    相关资源
    最近更新 更多