How to convert double* data to const char* or QByteArray efficiently - qt

I am trying to use the network programming APIs in Qt in my project. One part of my code requires me to convert double* data to QByteArray or a const char*.
I searched through the stackoverflow questions and could find many people suggesting this code :
QByteArray array(reinterpret_cast<const char*>(data), sizeof(double));
or, for an array of double :
QByteArray::fromRawData(reinterpret_cast<const char*>(data),s*sizeof(double));
When I use them in my function, It does notgive me the desired result. The output seems to be random characters.
Please Suggest an efficient way to implement it in Qt. Thank you very much for your time.
Regards
Alok

If you just need to encode and decode a double into a byte array, this works:
double value = 3.14159275;
// Encode the value into the byte array
QByteArray byteArray(reinterpret_cast<const char*>(&value), sizeof(double));
// Decode the value
double outValue;
// Copy the data from the byte array into the double
memcpy(&outValue, byteArray.data(), sizeof(double));
printf("%f", outValue);
However, that is not the best way to send data over the network, as it will depend on the platform specifics of how the machines encode the double type. I would recommend you look at the QDataStream class, which allows you to do this:
double value = 3.14159275;
// Encode the value into the byte array
QByteArray byteArray;
QDataStream stream(&byteArray, QIODevice::WriteOnly);
stream << value;
// Decode the value
double outValue;
QDataStream readStream(&byteArray, QIODevice::ReadOnly);
readStream >> outValue;
printf("%f", outValue);
This is now platform independent, and the stream operators make it very convenient and easy to read.

Assuming that you want to create a human readable string:
double d = 3.141459;
QString s = QString::number(d); // method has options for format and precision, see docs
or if you need localization where locale is a QLocale object:
s = locale.toString(d); // method has options for format and precision, see docs
You can easily convert the string into a QByteArray using s.toUtf8() or s.toLatin1() if really necessary. If speed is important there also is:
QByteArray ba = QByteArray::number(d); // method has options for format and precision, see docs

Related

QString to unicode std::string

I know there is plenty of information about converting QString to char*, but I still need some clarification in this question.
Qt provides QTextCodecs to convert QString (which internally stores characters in unicode) to QByteArray, allowing me to retrieve char* which represents the string in some non-unicode encoding. But what should I do when I want to get a unicode QByteArray?
QTextCodec* codec = QTextCodec::codecForName("UTF-8");
QString qstr = codec->toUnicode("Юникод");
std::string stdstr(reinterpret_cast<const char*>(qstr.constData()), qstr.size() * 2 ); // * 2 since unicode character is twice longer than char
qDebug() << QString(reinterpret_cast<const QChar*>(stdstr.c_str()), stdstr.size() / 2); // same
The above code prints "Юникод" as I've expected. But I'd like to know if that is the right way to get to the unicode char* of the QString. In particular, reinterpret_casts and size arithmetics in this technique looks pretty ugly.
The below applies to Qt 5. Qt 4's behavior was different and, in practice, broken.
You need to choose:
Whether you want the 8-bit wide std::string or 16-bit wide std::wstring, or some other type.
What encoding is desired in your target string?
Internally, QString stores UTF-16 encoded data, so any Unicode code point may be represented in one or two QChars.
Common cases:
Locally encoded 8-bit std::string (as in: system locale):
std::string(str.toLocal8Bit().constData())
UTF-8 encoded 8-bit std::string:
str.toStdString()
This is equivalent to:
std::string(str.toUtf8().constData())
UTF-16 or UCS-4 encoded std::wstring, 16- or 32 bits wide, respectively. The selection of 16- vs. 32-bit encoding is done by Qt to match the platform's width of wchar_t.
str.toStdWString()
U16 or U32 strings of C++11 - from Qt 5.5 onwards:
str.toStdU16String()
str.toStdU32String()
UTF-16 encoded 16-bit std::u16string - this hack is only needed up to Qt 5.4:
std::u16string(reinterpret_cast<const char16_t*>(str.constData()))
This encoding does not include byte order marks (BOMs).
It's easy to prepend BOMs to the QString itself before converting it:
QString src = ...;
src.prepend(QChar::ByteOrderMark);
#if QT_VERSION < QT_VERSION_CHECK(5,5,0)
auto dst = std::u16string{reinterpret_cast<const char16_t*>(src.constData()),
src.size()};
#else
auto dst = src.toStdU16String();
If you expect the strings to be large, you can skip one copy:
const QString src = ...;
std::u16string dst;
dst.reserve(src.size() + 2); // BOM + termination
dst.append(char16_t(QChar::ByteOrderMark));
dst.append(reinterpret_cast<const char16_t*>(src.constData()),
src.size()+1);
In both cases, dst is now portable to systems with either endianness.
Use this:
QString Widen(const std::string &stdStr)
{
return QString::fromUtf8(stdStr.data(), stdStr.size());
}
std::string Narrow(const QString &qtStr)
{
QByteArray utf8 = qtStr.toUtf8();
return std::string(utf8.data(), utf8.size());
}
In all cases you should have utf8 in std::string.
You can get the QByteArray from a UTF-16 encoded QString using this:
QTextCodec *codec = QTextCodec::codecForName("UTF-16");
QTextEncoder *encoderWithoutBom = codec->makeEncoder( QTextCodec::IgnoreHeader );
QByteArray array = encoderWithoutBom->fromUnicode( str );
This way you ignore the unicode byte order mark (BOM) at the beginning.
You can convert it to char * like:
int dataSize=array.size();
char * data= new char[dataSize];
for(int i=0;i<dataSize;i++)
{
data[i]=array[i];
}
Or simply:
char *data = array.data();

Does QBuffer write data in system dependent byte order?

The documentation says that QDataStream writes data in system independent way, but it says nothing about QBuffer. I develop a program that saves data in a file like this:
QByteArray a;
QBuffer b(&a);
b.open(QIODevide::WriteOnly);
quint32 x = 1;
b.write((char*)&x, sizeof(x));
b.close();
QFile f(...);
f.open(QIODevide::WriteOnly);
f.write(a.constData(), a.size());
f.close();
, and i want this file can be read in any other OS (win, linux, Mac OS). Will this code work or i must use QDataStream instead?
The QBuffer documentation says :
The QBuffer class provides a QIODevice interface for a QByteArray.
ie it is only a QByteArray underneath. On the other hand a QByteArray is portable because as long as you see the data as an array of byte and write one byte at a time you are fine. Your code will work:
When you say
I want this file to be read in any other OS
Is your file used by your program only or will it be used by other applications in the system? QDataStream provides nicer functions for I\O and you may be still able to take advantage of it.
It will be platform specific. x representation in memory depend on the endianess.It doesn't occur in the QBuffer, but when you do :
b.write((char*)&x, sizeof(x));
If you are on machines of different endianess, you will obtain different values for the resulting array by doing
char* data = &x;
qDebug()<< data[0];
qDebug()<< data[1];
qDebug()<< data[2];
qDebug()<< data[3];
Take a look at the source code of QDataStream operator
QDataStream &QDataStream::operator<<(qint32 i){
CHECK_STREAM_WRITE_PRECOND(*this)
if (!noswap) {
i = qbswap(i);
}
if (dev->write((char *)&i, sizeof(qint32)) != sizeof(qint32))
q_status = WriteFailed;
return *this;
}

How to convert QList<QByteArray> to QString in QT?

I have a QList<QByteArray> that I want to print out in a QTextBrowser. QTextBrowser->append() takes a QString.
Despite a ton of searching online, I have not found a way to convert the data I have into a QString.
There are several functions to convert QByteArray to QString: QString::fromAscii(), QString::fromLatin1(), QString::fromUtf8() etc. for the most common ones, and QTextCodec for other encodings. Which one is the correct one depends on the encoding of the text data in the byte array.
Try:
for(int i=0; i<list.size(); ++i){
QString str(list[i].constData());
// use your string as needed
}
from QByteArray to QString, do
const char * QByteArray::constData () const
Returns a pointer to the data stored in the byte array. The pointer
can be used to access the bytes that compose the array. The data is
'\0'-terminated. The pointer remains valid as long as the byte array
isn't reallocated or destroyed.
This function is mostly useful to pass a byte array to a function that
accepts a const char *.
you then have this QString constructor
QString ( const QChar * unicode )

Is there a way to receive data as unsigned char over UDP on Qt?

I need to send floating point numbers using a UDP connection to a Qt application. Now in Qt the only function available is
qint64 readDatagram ( char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0 )
which accepts data in the form of signed character buffer. I can convert my float into a string and send it but it will obviously not be very efficient converting a 4 byte float into a much longer sized character buffer.
I got hold of these 2 functions to convert a 4 byte float into an unsinged 32 bit integer to transfer over network which works fine for a simple C++ UDP program but for Qt I need to receive the data as unsigned char.
Is it possible to avoid converting the floatinf point data into a string and then sending it?
uint32_t htonf(float f)
{
uint32_t p;
uint32_t sign;
if (f < 0) { sign = 1; f = -f; }
else { sign = 0; }
p = ((((uint32_t)f)&0x7fff)<<16) | (sign<<31); // Whole part and sign.
p |= (uint32_t)(((f - (int)f) * 65536.0f))&0xffff; // Fraction.
return p;
}
float ntohf(uint32_t p)
{
float f = ((p>>16)&0x7fff); // Whole part.
f += (p&0xffff) / 65536.0f; // Fraction.
if (((p>>31)&0x1) == 0x1) { f = -f; } // Sign bit set.
return f;
}
Have you tried using readDatagram? Or converting the data to a QByteArray after reading? In many cases a char* is really just a byte array. This is one of those cases. Note that the writeDatagram can take a QByteArray.
Generally every thing sent across sockets is in bytes not strings, layers on either end do the conversions. Take a look here, especially the Broadcaster examples. They show how to create a QByteArray for broadcast and receive.
Not sure why the downvote, since the question is vague in requirements.
A 4-byte float is simply a 4 character buffer, if cast as one. If the systems are homogenous, the float can be sent as a signed char *, and bit for bit it'll be the same read into the signed char * on the receiver directly, no conversion needed. If the systems are heterogenous, then this won't work and you need to convert it to a portable format, anyway. IEEE format is often used, but my question is still, what are the requirements, is the float format the same between systems?
If I read it correctly, your primary question seems to be how to receive data of type unsigned char with QT's readDatagram function which uses a pointer to a buffer of type char.
The short answer is use a cast along these lines:
const size_t MAXSIZE = 1024;
unsigned char* data = malloc(MAXSIZE);
readDatagram ( (unsigned char *)data, MAXSIZE, address, port )
I'm going to assume you have multiple machines which use the same IEEE floating point format but some of which are big endian and some of which are little endian. See this SO post for a good discussion of this issue.
In that case you could do something a bit simpler like this:
const size_t FCOUNT = 256;
float* data = malloc(FCOUNT * sizeof(*data));
readDatagram ( (char *)data, FCOUNT * sizeof(*data), address, port )
for (int i = 0; i != FCOUNT; ++i)
data[i] = ntohf(*((uint32_t*)&data[i]));
The thing to remember is that as far as networking functions like readDatagram are concerned, the data is just a bunch of bits and it doesn't care what type those bits are interpreted as.
If both ends of your UDP connection use Qt, I would suggest looking at QDataStream. You can create this from a QByteArray each time you read a datagram, and then read whatever values you require - floats, maps, lists, QVariants, and of course string.
Similarly, on the sending side, you'd create a data stream, push data into it, then send the resulting QByteArray over writeDatagram.
Obviously this only works if both ends use Qt - the data encoding is well-defined, but non-trivial to generate by hand.
(If you want stream orientated behaviour, you could use the fact that QUDPSocket is a QIODevice with a data-stream, but it sounds as if you want per-datagram behaviour)

QByteArray to integer

As you may have figured out from the title, I'm having problems converting a QByteArray to an integer.
QByteArray buffer = server->read(8192);
QByteArray q_size = buffer.mid(0, 2);
int size = q_size.toInt();
However, size is 0. The buffer doesn't receive any ASCII character and I believe the toInt() function won't work if it's not an ASCII character. The int size should be 37 (0x25), but - as I have said - it's 0.
The q_size is 0x2500 (or the other endianness order - 0x0025).
What's the problem here ? I'm pretty sure q_size holds the data I need.
Something like this should work, using a data stream to read from the buffer:
QDataStream ds(buffer);
short size; // Since the size you're trying to read appears to be 2 bytes
ds >> size;
// You can continue reading more data from the stream here
The toInt method parses a int if the QByteArray contains a string with digits. You want to interpret the raw bits as an integer. I don't think there is a method for that in QByteArray, so you'll have to construct the value yourself from the single bytes. Probably something like this will work:
int size = (static_cast<unsigned int>(q_size[0]) & 0xFF) << 8
+ (static_cast<unsigned int>(q_size[1]) & 0xFF);
(Or the other way around, depending on Endianness)
I haven't tried this myself to see if it works but it looks from the Qt docs like you want a QDataStream. This supports extracting all the basic C++ types and can be created wth a QByteArray as input.
bool ok;
q_size.toHex().toInt(&ok, 16);
works for me
I had great problems in converting serial data (QByteArray) to integer which was meant to be used as the value for a Progress Bar, but solved it in a very simple way:
QByteArray data = serial->readall();
QString data2 = tr(data); //converted the byte array to a string
ui->QProgressBar->setValue(data2.toUInt()); //converted the string to an unmarked integer..
This works for me:
QByteArray array2;
array2.reserve(4);
array2[0] = data[1];
array2[1] = data[2];
array2[2] = data[3];
array2[3] = data[4];
memcpy(&blockSize, array2, sizeof(int));
data is a qbytearray, from index = 1 to 4 are array integer.
Create a QDataStream that operates on your QByteArray. Documentation is here
Try toInt(bool *ok = Q_NULLPTR, int base = 10) const method of QByteArray Class.
QByteArray Documentatio: http://doc.qt.io/qt-5/QByteArray.html

Resources