12

QByteArray一部がバイナリで一部がASCIIのGPSから受信したデータを保存する必要があります。デバッグの提案のために何が受信されているかを知りたいので、次のように書いてい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制限があるため、または終了文字などに達したために印刷が停止すると思いますが、推測に過ぎません。

この動作のヘルプまたは説明はありますか?

4

1 に答える 1

23

解決策/回避策:

実際、 のqDebug()出力は文字QByteArrayで切り捨てられ'\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" 

では、なぜこれが起こっているのですか?'\0'qDebug() を使用して出力できないのはなぜですか?

Qt の内部コードに飛び込んで、何qDebug()が行われているかを調べてみましょう。次のコード スニペットは、Qt 4.8.0 ソース コードからのものです。

このメソッドは、次の場合に呼び出されますqDebug() << buffer

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

上記stream->tsは typeQTextStreamであり、 を に変換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 ビット」バイナリ表現に変換します (これまでのところ、デバッグしたいバイナリ データがまだ残っているはずです)。

ただし、バイナリ データの長さを追加指定せずにメッセージ ハンドラに渡します。ご存知のように、文字を保持できるはずの C 文字列の長さを知ることは不可能'\0'です。(そのためQString::fromAscii()、上記のコードでは、バイナリ セーフのために追加の長さパラメーターが必要です。)

したがって、文字を処理したい場合は'\0'、独自のメッセージ ハンドラーを作成しても、長さが分からないため、問題は解決しません。悲しいけれど事実です。

于 2012-06-06T13:02:00.050 に答える