2012-06-06 10 views
12

Ho un QByteArray per memorizzare i dati ricevuti da un GPS, che è in parte binario e in parte ASCII. Voglio sapere a presentare proposte di debug sapere ciò che è stato ricevuto, così sto scrivendo un qDebug come questo:qDebug non stampa un QByteArray completo contenente dati binari

//QByteArray buffer; 
//... 
qDebug() << "GNSS msg (" << buffer.size() << "): " << buffer; 

E ho messaggi come questo in console:

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... 

Ma improvvisamente ho un nuova iterazione di stampa. I dati non sono stati ancora cancellati, è stato aggiunto. Quindi la dimensione del nuovo messaggio è per esempio 3204, più grande della precedente stampa ovviamente. Ma stampa esattamente lo stesso (ma con la nuova dimensione 3204 tra parentesi). Non ci sono nuovi dati vengono stampati, proprio come il messaggio precedente aveva:

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... 

immagino qDebug arresta la stampa perché ha un limite, o perché raggiunge un carattere di terminazione o qualcosa del genere, ma io sono solo indovinare.

Qualsiasi aiuto o spiegazione per questo comportamento?

risposta

23

Soluzione/soluzione:

Infatti, l'uscita del qDebug()QByteArray viene troncato al carattere '\0'. Questo non ha nulla a che fare con il QByteArray; non puoi nemmeno emettere un carattere '\ 0' usando qDebug(). Per una spiegazione vedi sotto.

QByteArray buffer; 
buffer.append("hello"); 
buffer.append('\0'); 
buffer.append("world"); 

qDebug() << "GNSS msg (" << buffer.size() << "): " << buffer; 

uscita:

GNSS msg (11): "hello 

Anche eventuali seguenti argomenti vengono ignorati:

qDebug() << "hello" << '\0' << "world"; 

uscita:

hello 

È possibile aggirare questo "problema", sostituendo il caratteri speciali nel tuo byte array prima di loro il debug:

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()! 

uscita:

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

Allora, perché sta succedendo questo? Perché non riesco a produrre uno '\0' usando qDebug()?

Entriamo nel codice interno Qt per scoprire cosa fa qDebug(). I seguenti frammenti di codice provengono dal codice sorgente di Qt 4.8.0.

Questo metodo viene chiamato quando si esegue qDebug() << buffer:

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

Il stream->ts sopra è di tipo QTextStream, che converte il QByteArray in un 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; 
} 

Come si può vedere, d->putString(QString) viene chiamato (il tipo di d è la classe privata interna del flusso di testo), che chiama write(QString) dopo aver eseguito alcuni padding per i campi a larghezza costante. Ho ignorare il codice di putString(QString) e saltare direttamente in d->write(QString), che è definito in questo modo:

inline void QTextStreamPrivate::write(const QString &data) 
{ 
    if (string) { 
     string->append(data); 
    } else { 
     writeBuffer += data; 
     if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) 
      flushWriteBuffer(); 
    } 
} 

Come si può vedere, la QTextStreamPrivate ha un buffer. Questo buffer è di tipo QString. Quindi cosa succede quando il buffer è finalmente stampato sul terminale? Per questo, dobbiamo scoprire cosa succede quando l'istruzione qDebug() termina e il buffer viene passato al gestore messaggi, che, per impostazione predefinita, stampa il buffer sul terminale. Questo sta accadendo nel distruttore della classe QDebug, che è definito come segue:

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

Così qui è la parte non-binary-safe. Qt prende il buffer testuale, lo converte in rappresentazione binaria "local 8bit" (fino ad ora, AFAIK dovremmo avere ancora i dati binari che vogliamo eseguire il debug).

Tuttavia, passa al gestore di messaggi senza le specifiche aggiuntive della lunghezza dei dati binari. Come dovresti sapere, è impossibile scoprire la lunghezza di una stringa C che dovrebbe anche essere in grado di contenere i caratteri '\0'. (Ecco perché QString::fromAscii() nel codice sopra ha bisogno il parametro della lunghezza aggiuntiva per binary-sicurezza.)

Quindi, se si desidera gestire i '\0' personaggi, anche la scrittura del proprio gestore di messaggi non risolverà il problema, come non si può conoscere la lunghezza. Triste ma vero.

+0

Non vorrei citare la parola "problema", sembra davvero un bug (come evidenziato dalla chiusura mancante "dopo la stringa che normalmente qDebug aggiunge. –

+0

@fish Penso anche che sia sufficiente troncare i dati in un" \ 0'' non è bello, d'altra parte, immagino che le uscite qDebug siano fatte per dati testuali (almeno per QString e QByteArray). Si potrebbe sostenere che in caso di un QByteArray, questo * non dovrebbe * essere supposto, ma poi si può anche sostenere che Qt potrebbe non sapere come si desidera eseguire il debug dei dati binari. L'output di un carattere zero funziona ma non si può leggerlo (che è lo scopo di qDebug()). Forse un .toHex() è la soluzione migliore per i dati binari in alcuni casi, quindi suppongo che Qt lo lasci * come * per emettere i dati – leemes

+0

Oltre a ciò si può sostenere che questo è un comportamento corretto, sono abbastanza sicuro non era intenzionale, altrimenti lo sarebbe aggiungi il mancante ". –