Ответ 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'
, даже написать собственный обработчик сообщений не решит проблему, так как вы не можете знать длину. Печально, но верно.