如何解压 QByteArray 中的 32 位整数?

How to unpack 32bit integer packed in a QByteArray?

我正在使用串行通信,我在 QByteArray 中接收到 32 位整数,这些整数被打包成 4 个单独的字节(小端)。 我尝试使用 QByteArray::toLong() 从 4 个字节中解压值,但转换失败并且 returns 错误的数字:

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

预期的 unpacked_value 是 0x78563412(小端解包)。为什么转换失败?

toLong() 将 char * digits string 转换为 long。不是字节。并且您的值可能不会构成字符串“0x78563412”或其等效的十进制值。因此结果为 0。

如果您需要解释字节值,只要您可以执行以下操作:

long value;
value == *((long*)packed_bytes.data());

或者访问一个字节数组作为长数组:

long * values;
values == (long*)packed_bytes.data();

values[0]; // contains first long
values[1]; // contains second long
...

不知道我的例子是否开箱即用,但应该说清楚了原理。

看看这个例子:

char bytes[] = {255, 0};

QByteArray b(bytes, 2);

QByteArray c("255");

qDebug() << b.toShort() << c.toShort();

qDebug() << *((short*)b.data()) << *((short*)c.data());

输出是:

0 255 
255 13618

您可能需要根据字节序更改字节顺序。但它可以满足您的需求。

您可以使用位操纵器构建您的 qint64:

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

您可以使用QDataStream读取二进制数据。

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

这是一个 generic 解决方案,用于将 QByteArray 转换为 "some other type"(例如问题中特别提出的问题)运行它通过 QDataStream (由接受的答案完成)。

DISCLAIMER: I am only advocating for using this in a private implementation. I am aware there are many ways one could abuse the macro!

使用这个宏,你可以很容易地产生很多转换函数,比如我提供的例子。如果您需要从流中提取多种类型,以这种方式定义一系列此类函数可能会很有用。显然,您可以针对您的用例调整宏,重点是 pattern 可以保持基本相同并放入这样的宏中。

#define byteArrayToType( data, order, type ) \
    QDataStream stream( data ); \
    stream.setByteOrder( order ); \
    type t; \
    stream >> t; \
    return t;

简单包装宏的示例函数:

16 位,有符号

qint16 toQInt16( const QByteArray &data,
                 const QDataStream::ByteOrder order=QDataStream::BigEndian )
{ byteArrayToType( data, order, qint16 ) }

32 位,有符号

qint32 toQInt32( const QByteArray &data,
                 const QDataStream::ByteOrder order=QDataStream::BigEndian )
{ byteArrayToType( data, order, qint32 ) }

64 位,有符号

qint64 toQInt64( const QByteArray &data,
                 const QDataStream::ByteOrder order=QDataStream::BigEndian )
{ byteArrayToType( data, order, qint64 ) }

将字节数组转换为所需格式并使用built-in 函数qFromBigEndianqFromLittleEndian 设置字节顺序。示例代码如下所示,

QByteArray byteArray("\x45\x09\x03\x00");
quint32 myValue = qFromBigEndian<quint32>(byteArray);
qDebug() << "Hex value: " << QString("0x%1").arg(myValue, 8, 16, QLatin1Char( '0' ));

myValue 保留转换后的值。

不要忘记包含头文件<QtEndian>