Подтвердить что ты не робот

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 останавливает печать, потому что у нее есть предел, или потому, что она достигает завершающего символа или что-то в этом роде, но я только предполагаю.

Любая помощь или объяснение этого поведения?

4b9b3361

Ответ 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 берет текстовый буфер, преобразует его в двоичное представление "local 8bit" (до сих пор AFAIK мы все равно должны иметь двоичные данные, которые мы хотим отлаживать).

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

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