原文链接:【Qt】Qt存储大整数到`JsonValue`【2023.08.01】_在qt中,当您将一个uint64数据存储为qjsonvalue,如果该数据超出了javascript-CSDN博客
Qt存储大整数到JsonValue
简介
我负责的需求是这样,存储文件绝对路径和文件大小到json
字符串。格式如下:
{ "fileList": { "path": "dsadadadadadad", "size": 222 } }
复制
开发过程中,未意识到文件可能大于int的最大值,所以埋下了隐患。今天被测试提了个bug。主要原因是QJsonValue的toInt()返回的是4字节的int类型,拿来存一个大于有符号四字节整数的值会溢出。这种边界值界定不明确导致bug的情况还是比较常见,做个总结分享帮助大家避坑。
此外就是csdn上对于这块的教程属实是又少又粗略。
上代码
/** * 本程序是测试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.QJsonValue的toInt()是提供了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.对于特别大的数,保证精度首选字符串。