前述
在一些需求中,我们需要读取一些二进制文件,比如java的class文件,图片文件等等。我们有时需要解析里面具体的内容。通过Qt的QFile类读取文件后,可以将其中的二进制读到一个QBytearray中,而我们可以通过QBytearray的toHex方法将其转换为16进制字符串。
而有些时候,这些数据会存储一些double和float类型的浮点数据的16进制形式,我们需要根据需求把16进制转换为double或float。
对于整数int形式,Qt中有明确的方法可以进行转换。这里我们用uint的类型,避免负数。
#include <QString>
int main(){
// do some things
QString hex = "cafebabe";
uint res = hex.toUInt(nullptr, 16);
qDebug() << res; // 3405691582
// do some things
}
但是,对于float和double类型的数据,QString没有对应的16进制转换方法。需要另辟蹊径。
需要知道
首先我们需要知道c++中double和float类型所占的空间。
double在内存中占8字节,float在内存中占4字节。
其次,我们需要知道一些东西:
int、uint占4字节
最后,我们需要知道QT中内置的类型:
qulonglong,占8字节
对于qulonglong,在qt中定义如下:
typedef long long qint64; /* 64 bit signed */
typedef unsigned long long quint64; /* 64 bit unsigned */
typedef qint64 qlonglong;
typedef quint64 qulonglong;
(见qglobal.h)
qulonglong就是unsigned long long
float和double转16进制
基本思路
在Qt中,QString存在一个arg方法,这个方法有n多种重载,它的作用类似字符串模板函数,并带有进制转换的功能,arg函数的所有重载都有4个参数。
如下:
QString
arg(const QString &a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
QString
arg(qlonglong a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString
arg(qulonglong a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString
arg(long a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString
arg(ulong a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString
arg(int a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString
arg(uint a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString
arg(short a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString
arg(ushort a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString
arg(double a, int fieldWidth = 0, char format = 'g', int precision = -1, QChar fillChar = QLatin1Char(' ')) const
QString
arg(char a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
QString
arg(QChar a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
QString
arg(QStringView a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
QString
arg(QLatin1String a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
QString
arg(Args &&... args) const
举个例子,我们需要将一个int类型的1000转换成16进制字符串,字符串长度8,左侧补0:
QString hex = QString("%1").arg(1000, 8, 16, QLatin1Char('0'));
qDebug() << hex; // "000003e8"
这很简单,但遗憾的是,arg方法并没有第一个参数是float和double的重载。
于是乎,我们可以将float和double转换成arg重载函数可用的类型。
但是,如果单纯地强制转换,会造成数据丢失,我们需要操作内存,将float和double的指针转换成其他类型的指针,再解引用。
但这里需要注意的是:
float占4字节,我们可以把float指针转换成int指针。
但是,对于double,他占8字节,就不能用int或uint,存不下,int和uint只占4字节,类型转换会造成数据丢失,结果错误。
所以,double指针必须转换到占用空间相等或比他大的数据类型指针。
double占8字节,我们可以选择qulonglong类型进行转换。
float取地址后,转换为int指针或uint指针类型,,在解引用得到int或uint;
double取地址后,转为qulonglong指针类型,再解引用得到qulonglong。
float a = 1.23f;
double b = 1.234;
// float指针类型转换
uint a_uint = *(uint*)&a;
// double指针类型转换
qulonglong b_q = *(qulonglong*)&b
而后就可以放入arg了。
对于16进制转float和double,只需要逆着来,先把16进制使用toUInt方法转成uint,(double是toLonglong)再使用指针类型强制转换,得到float或double。
完整代码
// float转16进制字符串
float f = 20.321;
uint f_uint = *(uint*)&f;
QString f_hex = QString("%1").arg(f_uint, 4, 16, QLatin1Char('0')); // 4是生成字符串的最小长度,可以改为8
qDebug() << f_hex; // "41a29168"
// 16进制字符串转float
QString hex_float = "41a29168";
uint hex_uint = hex_float.toUInt(nullptr, 16);
float hex_res = *(float*)&hex_uint;
qDebug() << hex_res; // 20.321
// double转16进制字符串
double d = 20.321;
qulonglong d_long = *(qulonglong*)&d;
QString d_hex = QString("%1").arg(d_long, 8, 16, QLatin1Char('0'));
qDebug() << d_hex; // "4034522d0e560419"
// 16进制字符串转double
QString hex_double = "4034522d0e560419";
qulonglong hex_long = hex_double.toULongLong(nullptr, 16);
double hex_d = *(double*)&hex_long;
qDebug() << hex_d; // 20.321