qDebug не печатает полный QByteArray, содержащий двоичные данные

У меня есть 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 ответ

Решение

Решение / обходной путь:

Действительно, qDebug() вывод QByteArray усекается на '\0' персонаж. Это не имеет ничего общего с QByteArray; вы даже не можете вывести символ '\0', используя qDebug(). Для объяснения см. Ниже.

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" 

Так почему же это происходит? Почему я не могу вывести '\0' используя qDebug()?

Давайте погрузимся во внутренний код Qt, чтобы узнать, что qDebug() делает. Следующие фрагменты кода взяты из исходного кода Qt 4.8.0.

Этот метод вызывается, когда вы делаете 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 класс, который определяется следующим образом:

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, у нас все еще должны быть двоичные данные, которые мы хотим отлаживать).

Но затем передает его в обработчик сообщений без дополнительной спецификации длины двоичных данных. Как вы должны знать, невозможно определить длину C-строки, которая также должна быть в состоянии содержать '\0' персонажи. (Вот почему QString::fromAscii() в приведенном выше коде необходим дополнительный параметр длины для бинарной безопасности.)

Так что если вы хотите справиться с '\0' символы, даже написание собственного обработчика сообщений не решит проблему, так как вы не можете знать длину. Грустно, но правда.

Другие вопросы по тегам