Converting QByteArray to hex, bin and char - qt

I have been trying to convert QByteArray data into a hex, bin and char tabular representation, but I encounter problems when in the QByteArray there are escape sequences... for example if I have the QByTeArray which contains "Hello World" the space is not converted into Hex, but it remains a space... what do I do?
for (int i = 0; i < n; i++){
std::cout << "0x" << QString::number(0, 16).toStdString() << "\t";
if (((i+1) % 8) == 0)
std::cout << std::endl;
}
this is the code used for example to run through the QByteArray and transform it into a hex representation.
Btw, I am using QT creator to program in C++ and I'm a beginner
I tried converting the QByteArray into a QString containing the translation into ASCII of the data, so that then maybe with an if else explain the behaviour the program should have every it encounters a number from 00 to 32... but it requires massive effort. Isn't there a shortcut?

In your example, you never use a QByteArray but instead output 0x0 for n iterations.
Assuming, you have some array containing "Hello World", your loop correctly outputs hex values iff you use i to access the array elements and .length() instead of n:
const QByteArray data = "Hello World";
for (int i = 0; i < data.length(); i++){
std::cout << "0x" << QString::number(data[i], 16).toStdString() << "\t";
if (((i+1) % 8) == 0)
std::cout << std::endl;
}
Output:
0x48 0x65 0x6c 0x6c 0x6f 0x20 0x57 0x6f
0x72 0x6c 0x64

The QByteArray class already has QByteArray::toHex() method.
QByteArray arr = "Hello world";
std::cout << QString("0x" + arr.toHex(':')).replace(":", " 0x").toStdString();

Related

Why are there double quotes when we print using QString?

I am using Qt framework in Linux and a complete beginner.
When I print a simple message like:
qDebug() << "Hello World";
In the Console the output is Hello World.
But if I print the same message like:
QString str = "Hello World";
qDebug() << str;
In the Console the output is "Hello World",(Notice the quotes), How to get the same output using QString?
See QDebug::noquote
Disables automatic insertion of quotation characters around QChar, QString and QByteArray contents and returns a reference to the stream.
When quoting is disabled, these types are printed without quotation characters and without escaping of non-printable characters.
This function was introduced in Qt 5.4.
Usage:
QString str = "Hello World";
qDebug().noquote() << str;
http://doc.qt.io/qt-5/qdebug.html#noquote
you can find the answer in the Qt source code(detail of implementation in the link below) :
https://code.woboq.org/qt5/qtbase/src/corelib/io/qdebug.h.html
you have several definitions of the << operator
from Qt source code :
/*! \fn QDebug &QDebug::operator<<(const char *s)
Writes the
'\0'-terminated string s, to the stream and returns a reference
to the stream. The string is never quoted nor transformed to the
output, but note that some QDebug backends might not be 8-bit clean.
/ /! \fn QDebug &QDebug::operator<<(const QString &s)
Writes the string, \a s, to the stream and returns a reference to the stream.
Normally, QDebug prints the string inside quotes and transforms
non-printable characters to their Unicode values (\u1234). To print
non-printable characters without transformation, enable the noquote()
functionality. Note that some QDebug backends might not be 8-bit
clean. Output examples: \code QString s; s = "a"; qDebug().noquote()
<< s; // prints: a qDebug() << s; // prints: "a" s = "\"a\r\n\"";
qDebug() << s; // prints: "\"a\r\n\"" s = "\033"; // escape character
qDebug() << s; // prints: "\u001B" s = "\u00AD"; // SOFT HYPHEN
qDebug() << s; // prints: "\u00AD" s = "\u00E1"; // LATIN SMALL LETTER
A WITH ACUTE qDebug() << s; // prints: "á" s = "a\u0301"; // "a"
followed by COMBINING ACUTE ACCENT qDebug() << s; // prints: "aÌ"; s =
"\u0430\u0301"; // CYRILLIC SMALL LETTER A followed by COMBINING ACUTE
ACCENT qDebug() << s; // prints: "аÌ" \endcode
*/
qDebug()<< "hello world" uses QDebug &QDebug::operator<<(const char *s) and not QDebug &QDebug::operator<<(const QString &s) that's why you get the quotes in one version and not the other.
you can get the same result by using :
qDebug().noquote() << s;
on the QString version

How to unpack 32bit integer packed in a QByteArray?

I'm working with serial communication, and I receive 32bit integers in a QByteArray, packed in 4 separate bytes (little-endian).
I attempt to unpack the value from the 4 bytes using QByteArray::toLong() but it fails the conversion and returns the wrong number:
quint8 packed_bytes[] { 0x12, 0x34, 0x56, 0x78 };
QByteArray packed_array { QByteArray(reinterpret_cast<char*>(packed_bytes),
sizeof(packed_bytes)) };
bool isConversionOK;
qint64 unpacked_value { packed_array.toLong(&isConversionOK) };
// At this point:
// unpacked_value == 0
// isConversionOK == false
The expected unpacked_value is 0x78563412 (little-endian unpacking). Why is the conversion failing?
You can use a QDataStream to read binary data.
quint8 packed_bytes[] { 0x12, 0x34, 0x56, 0x78 };
QByteArray packed_array { QByteArray(reinterpret_cast<char*>(packed_bytes), sizeof(packed_bytes)) };
QDataStream stream(packed_array);
stream.setByteOrder(QDataStream::LittleEndian);
int result;
stream >> result;
qDebug() << QString::number(result,16);
toLong() converts a char * digits string to long. Not bytes. And your values likely don't make the up the string "0x78563412" or its decimal equivalent. Hence the 0 result.
If you need the byte values interpreted as long you can do something like:
long value;
value == *((long*)packed_bytes.data());
Or to access an array of bytes as long array:
long * values;
values == (long*)packed_bytes.data();
values[0]; // contains first long
values[1]; // contains second long
...
Don't know whether my examples work out of the box but it should make clear the principle.
Check out this example:
char bytes[] = {255, 0};
QByteArray b(bytes, 2);
QByteArray c("255");
qDebug() << b.toShort() << c.toShort();
qDebug() << *((short*)b.data()) << *((short*)c.data());
the output is:
0 255
255 13618
You may need to change the byte order depending on the endianess. But it does what you need.
you can build your qint64 with bit manipulators:
#include <QtGlobal>
#include <QByteArray>
#include <QDebug>
int main()
{
quint8 packed_bytes[] { 0x12, 0x34, 0x56, 0x78 };
QByteArray packed_array { QByteArray(reinterpret_cast<char*>(packed_bytes),
sizeof(packed_bytes)) };
qint64 unpacked_value = 0;
unpacked_value |= packed_array.at(0) |
packed_array.at(1) << 8 |
packed_array.at(2) << 16 |
packed_array.at(3) << 24;
qDebug() << QString("0x%1").arg(unpacked_value, 0, 16);
}
Here's a generic solution for converting a QByteArray to "some other type" (such as what is specifically asked in the question) by running it through a QDataStream (as done by the accepted answer).
DISCLAIMER: I am only advocating for using this in a private implementation. I am aware there are many ways one could abuse the
macro!
Using this macro, you can easily produce many conversion functions such as the examples I've provided. Defining a series of such functions in this way may be useful if you need to pull a variety of types out of a stream. Obviously, you could tweak the macro for your use case, the point is the pattern can remain basically same and be put in a macro like this.
#define byteArrayToType( data, order, type ) \
QDataStream stream( data ); \
stream.setByteOrder( order ); \
type t; \
stream >> t; \
return t;
Example functions, which simply wrap the macro:
16 bit, signed
qint16 toQInt16( const QByteArray &data,
const QDataStream::ByteOrder order=QDataStream::BigEndian )
{ byteArrayToType( data, order, qint16 ) }
32 bit, signed
qint32 toQInt32( const QByteArray &data,
const QDataStream::ByteOrder order=QDataStream::BigEndian )
{ byteArrayToType( data, order, qint32 ) }
64 bit, signed
qint64 toQInt64( const QByteArray &data,
const QDataStream::ByteOrder order=QDataStream::BigEndian )
{ byteArrayToType( data, order, qint64 ) }
Cast the Byte array to the required format and use the built-in function qFromBigEndian or qFromLittleEndian to set the Byte order. Example code is shown below,
QByteArray byteArray("\x45\x09\x03\x00");
quint32 myValue = qFromBigEndian<quint32>(byteArray);
qDebug() << "Hex value: " << QString("0x%1").arg(myValue, 8, 16, QLatin1Char( '0' ));
myValue holds the converted value.
Don't forget to include the header file <QtEndian>

Why is QString printed with quotation marks?

So when you use qDebug() to print a QString, quotation marks appears suddenly in the output.
int main()
{
QString str = "hello world"; //Classic
qDebug() << str; //Output: "hello world"
//Expected Ouput: hello world
}
I know we can solve this with qPrintable(const QString), but I was just wondering why does QString work like that?, and Is there a method inside QString to change the way it's printed?
Qt 5.4 has a new feature that lets you disable this. To quote the documentation:
QDebug & QDebug::​noquote()
Disables automatic insertion of quotation characters around QChar, QString and QByteArray contents and returns a reference to the
stream.
This function was introduced in Qt 5.4.
See also quote() and maybeQuote().
(Emphasis mine.)
Here's an example of how you'd use this feature:
QDebug debug = qDebug();
debug << QString("This string is quoted") << endl;
debug.noquote();
debug << QString("This string is not") << endl;
Another option is to use QTextStream with stdout. There's an example of this in the documentation:
QTextStream out(stdout);
out << "Qt rocks!" << endl;
Why?
It's because of the implementation of qDebug().
From the source code:
inline QDebug &operator<<(QChar t) { stream->ts << '\'' << t << '\''; return maybeSpace(); }
inline QDebug &operator<<(const char* t) { stream->ts << QString::fromAscii(t); return maybeSpace(); }
inline QDebug &operator<<(const QString & t) { stream->ts << '\"' << t << '\"'; return maybeSpace(); }
Therefore,
QChar a = 'H';
char b = 'H';
QString c = "Hello";
qDebug()<<a;
qDebug()<<b;
qDebug()<<c;
outputs
'H'
H
"Hello"
Comment
So why Qt do this? Since qDebug is for the purpose of debugging, the inputs of various kinds of type will become text stream output through qDebug.
For example, qDebug print boolean value into text expression true / false:
inline QDebug &operator<<(bool t) { stream->ts << (t ? "true" : "false"); return maybeSpace(); }
It outputs true or false to your terminal. Therefore, if you had a QString which store true, you need a quote mark " to specify the type.
Qt 4: If the string contains just ASCII, the following workaround helps:
qDebug() << QString("TEST").toLatin1().data();
Simply cast to const char *
qDebug() << (const char *)yourQString.toStdString().c_str();
one liner no quotes: qDebug().noquote() << QString("string");

Size of QByteArray is hard to compute?

Recently I programmed to do file transmission with Qt. Thought it worked now, I'm still curious about what happened. Please help me find out the reason. Many thanks.
Why the size of head is bigger than the sum of sizeof(qin32), sizeof(qint32) and length of file name?(I guess it is the reason of function - setVersion())
QFileInfo info(file_to_send.fileName());
QByteArray head;
QDataStream out(&head, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_0);
out << qint32(file_to_send.size() + info.fileName().length() + sizeof(qint32)*2)
<< qint32(info.fileName().length())
<< info.fileName();
tcpClient.write(head);
You have made it to complicated. Pattern is like that:
QFileInfo info(file_to_send.fileName());
QByteArray head;
QDataStream out(&head, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_0);
out << qint32(0)
<< info.fileName(); // << YouCanAddMoreStuffHere;
out.device()->seek(0);
out << qint32(out.length());
tcpClient.write(head);
and read code:
void SomeClass::slotReadClient() { // slot connected to readyRead signal of QTcpSocket
QTcpSocket *tcpSocket = (QTcpSocket*)sender();
QDataStream clientReadStream(tcpSocket);
while(true) {
if (!next_block_size) {
if (tcpSocket->bytesAvailable() < sizeof(qint32)) { // are size data available
break;
}
clientReadStream >> next_block_size;
}
if (tcpSocket->bytesAvailable() < next_block_size) {
break;
}
QString fileName;
clientReadStream >> fileName; // >> YouCanAddMoreStuffHere; // same as above
next_block_size = 0;
}
}
info.filename() writes out its own length
if you don't want that then you can do
QFileInfo info(file_to_send.fileName());
QByteArray head;
QDataStream out(&head, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_0);
QByteArray filename = info.fileName().toLatin1();
out << qint32(file_to_send.size() + filename .length() + sizeof(qint32)*2);
<< qint32(filename .length())
out.writeRawData(fileName.constData(), filename.length());
tcpClient.write(head);
using writeRawData which bypasses any built in encoding

QDataStream Serialization Problem

Here goes my Code first.
QByteArray buff;
QDataStream stream(&buff, QIODevice::ReadWrite);
stream.setVersion(QDataStream::Qt_4_7);
stream << 5;
stream << 6;
qDebug() << buff;
int x;
int y;
stream >> x >> y;
qDebug() << x << y;
I expect x be 5 and y be 6. But its showing 0 0
Here is the output
"
0 0
As Frank mentioned the QDataStream is still at the end position (after writing your data). If you don't want to create a new stream, it should also be possible to call stream.reset() to put the stream's internal position to the beginning. Or something like stream.seek(0).
Try this:
QByteArray buff;
QDataStream stream(&buff, QIODevice::ReadWrite);
stream.setVersion(QDataStream::Qt_4_0);
stream << 5;
stream << 6;
qDebug() << buff.toHex();
int x;
int y;
// This line will move the internal QBuffer to position 0
stream.device()->reset();
stream >> x >> y;
qDebug() << x << y;
Output:
"0000000500000006"
5 6
You can't read/write like this from/to a QByteArray using a single QDataStream, at the same time, due to the data stream's internal state (stream position). Try with a second QDataStream for reading.

Resources