【Qt】Qt存储大整数到`JsonValue`【2023.08.01】

Qt存储大整数到JsonValue

简介

  我负责的需求是这样,存储文件绝对路径和文件大小到json字符串。格式如下:

json 复制代码
{
    "fileList": {
        "path": "dsadadadadadad",
        "size": 222
    }
}

开发过程中,未意识到文件可能大于int的最大值,所以埋下了隐患。今天被测试提了个bug。主要原因是QJsonValuetoInt()返回的是4字节的int类型,拿来存一个大于有符号四字节整数的值会溢出。这种边界值界定不明确导致bug的情况还是比较常见,做个总结分享帮助大家避坑。

此外就是csdn上对于这块的教程属实是又少又粗略。

上代码

c 复制代码
/**
 * 本程序是测试Qt 程序存储64位整数 至Json文件 并加载读出
 */
#include <QtCore/QCoreApplication>
#include <QJsonObject>
#include <QJsonValue>
#include <QDebug>
#include <QDir>
#include <QJsonDocument>
#include <QByteArray>
#include <iostream>
#include <climits>
#include <cfloat>
#include <iomanip>

void printTypeInfo();
/**
 *
 Type    Size(bytes)           Minimum               Maximum
 ------------------------------------------------------------------------------------------------------------
 bool    1
 char    1                     -128                  127
 int     4                     -2147483648           2147483647
 uint    4                     4294967295
 short   2                     -32768                32767
 ushort  2                     65535
 long    4                     -2147483648           2147483647
 ulong   4                     4294967295
 ll      8                     -9223372036854775808  9223372036854775807
 ull     8                     18446744073709551615
 float   4                     1.17549e-038          3.40282e+038
 double  8                     2.22507e-308          1.79769e+308
 ldouble 8                     2.22507e-308          1.79769e+308
 ------------------------------------------------------------------------------------------------------------
 */
int main(int argc, char *argv[])
{
	QCoreApplication a(argc, argv);

	//printTypeInfo();

	using namespace std;


	/************************************************************************/
	/*
	*	第一部分、往文件中写
	*/
	/************************************************************************/
	// 创建JSON对象
	QJsonObject root;

	// 创建fileList数组

	QJsonObject obj;
	obj["path"] = "YangNaifeng";
	qint64 llData =  1;// LLONG_MAX;
	obj["size"] = llData;//最大15位

	root.insert("fileList", obj);

	QJsonDocument doc;
	doc.setObject(root);

	auto path = QCoreApplication::applicationDirPath();
	auto outJson = path + "/" + "sad.json";

	QDir dir(path);
	if (dir.mkpath(path))
	{
		QFile file(outJson);
		file.open(QIODevice::WriteOnly);
		file.write(doc.toJson());
		file.close();
	}
	else
	{
		qCritical() << QString::fromStdWString(L"创建文件夹失败!");
	}


	/************************************************************************/
	/*
	*	从文件中读 并解析
	*/
	/************************************************************************/
	QFile readFile(outJson);
	readFile.open(QIODevice::ReadOnly);
	QByteArray data = readFile.readAll();
	QJsonDocument docRead = QJsonDocument::fromJson(data);

	if (!docRead.isNull() && docRead.isObject()) {

		QJsonObject obj = docRead.object();

		if (obj.contains("fileList") && obj["fileList"].isObject()) {

			QJsonObject fileListObj = obj["fileList"].toObject();

			QString path = fileListObj["path"].toString();
			
			auto eDoubleType = QJsonValue::Double;
			cout << fileListObj["size"].type();//QJsonValue::Double
			auto size = fileListObj["size"].toDouble();


			qint64  nSize = QString::number(size, 'f', 0).toLongLong();
			qDebug() << "Path:" << path;
			qDebug() << "Size:" << size;

		}
		else
		{
			qDebug() << "fileList not found";
		}

	}
	else
	{
		qDebug() << "Invalid JSON document";
	}


	return a.exec();
}



void printTypeInfo()
{
	using namespace std;

		// 设置表格样式
		cout << left;
		cout << setw(8) << "Type";
		cout << setw(22) << "Size(bytes)";
		cout << setw(22) << "Minimum";
		cout << setw(22) << "Maximum";
		cout << endl;

		cout << "------------------------------------";
		cout << "------------------------------------";
		cout << "------------------------------------";
		cout << endl;

		// bool
		cout << setw(8) << "bool";
		cout << setw(22) << sizeof(bool);
		cout << setw(22);
		cout << setw(22);
		cout << endl;

		// char
		cout << setw(8) << "char";
		cout << setw(22) << sizeof(char);
		cout << setw(22) << (int)CHAR_MIN;
		cout << setw(22) << (int)CHAR_MAX;
		cout << endl;

		// int
		cout << setw(8) << "int";
		cout << setw(22) << sizeof(int);
		cout << setw(22) << INT_MIN;
		cout << setw(22) << INT_MAX;
		cout << endl;

		// unsigned int
		cout << setw(8) << "uint";
		cout << setw(22) << sizeof(unsigned int);
		cout << setw(22);
		cout << setw(22) << UINT_MAX;
		cout << endl;

		// short
		cout << setw(8) << "short";
		cout << setw(22) << sizeof(short);
		cout << setw(22) << SHRT_MIN;
		cout << setw(22) << SHRT_MAX;
		cout << endl;

		// unsigned short
		cout << setw(8) << "ushort";
		cout << setw(22) << sizeof(unsigned short);
		cout << setw(22);
		cout << setw(22) << USHRT_MAX;
		cout << endl;

		// long
		cout << setw(8) << "long";
		cout << setw(22) << sizeof(long);
		cout << setw(22) << LONG_MIN;
		cout << setw(22) << LONG_MAX;
		cout << endl;

		// unsigned long
		cout << setw(8) << "ulong";
		cout << setw(22) << sizeof(unsigned long);
		cout << setw(22);
		cout << setw(22) << ULONG_MAX;
		cout << endl;

		// long long 
		cout << setw(8) << "ll";
		cout << setw(22) << sizeof(long long);
		cout << setw(22) << LLONG_MIN;
		cout << setw(22) << LLONG_MAX;
		cout << endl;

		// unsigned long long
		cout << setw(8) << "ull";
		cout << setw(22) << sizeof(unsigned long long);
		cout << setw(22);
		cout << setw(22) << ULLONG_MAX;
		cout << endl;

		// float
		cout << setw(8) << "float";
		cout << setw(22) << sizeof(float);
		cout << setw(22) << FLT_MIN;
		cout << setw(22) << FLT_MAX;
		cout << endl;

		// double
		cout << setw(8) << "double";
		cout << setw(22) << sizeof(double);
		cout << setw(22) << DBL_MIN;
		cout << setw(22) << DBL_MAX;
		cout << endl;

		// long double
		cout << setw(8) << "ldouble";
		cout << setw(22) << sizeof(long double);
		cout << setw(22) << LDBL_MIN;
		cout << setw(22) << LDBL_MAX;
		cout << endl;

		cout << "------------------------------------";
		cout << "------------------------------------";
		cout << "------------------------------------";

}

总结

1.QJsonValue存储整数是按照double类型存储的,即使是1这么一个很小的数。

2.QJsonValuetoInt()是提供了double到int的转换。而不是文件本身存的就是int类型的数据。

3.QJsonValue之所以没有tolonglong()的接口是因为double类型是有精度损失的。double类型的有效数字位数:15-16位数字,double类型的有效数字位数是15位还是16位,主要取决于浮点数的值:a. 对于绝对值在1.0和2^53之间的正常值,double类型一般能表示15位有效数字。b. 当浮点数接近0时,指数部位全部为0,此时有效数字位数可以达到16位。3. 当浮点数接近2^53时,指数部位为最大值,此时有效数字位数只有15位。

4.为啥Qt存整数到Json不是按整形存,而是以double类型存?

为什么JSON不支持 int64 类型?

通过上面的介绍有两个关键点:

  1. JSON 是基于 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集
  2. JSON 支持number 类型

Javascript的数字存储使用了IEEE 754中规定的双精度浮点数数据类型,而这一数据类型能够安全存储 -(2^53-1) 到 2^53-1 之间的数值(包含边界值)。JSON 是 Javascript 的一个子集,所以它也遵守这个规则。

以下是rfc7159的说明:

Note that when such software is used, numbers that are integers and are in the range -(2\^53)+1, (2\^53)-1 are interoperable in the sense that implementations will agree exactly on their numeric values.

这两个边界值可以通过 JavaScript 的 Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER 获取。

5.对于特别大的数,保证精度首选字符串。

相关推荐
用户805533698031 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner2 小时前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz5 天前
QML Hello World 入门示例
qt
xcyxiner8 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner9 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner9 天前
DicomViewer (添加模型类)3
qt
xcyxiner10 天前
DicomViewer (目录调整) 2
qt
xcyxiner10 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00612 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术12 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript