在C++中读取二进制文件时将char*转换为double

Converting char * to double while reading from binary file in C++?

本文关键字:char 转换 double C++ 读取 二进制文件      更新时间:2023-10-16

我正在编写一个二进制I/O,用于在应用程序中存储数据。

为了说明,考虑我想将大小为10的双数组存储到文件中。

现在,由于不能保证double在所有平台上都使用8个字节,因此需要对文件的读取器进行一些修改。虽然我使用的是Qt,但我认为问题主要在于将char*中读取的数据转换为double的方式。读取的数据几乎为零。

例如,1读取为2.08607954259741e-317。

为什么每个双精度都被读取为零,甚至认为不是

void FileString::SaveBinary()
{
QFile *file = new QFile(fileName);
if (!file->open(QFile::WriteOnly))
{
QString err = file->errorString();
QString *msgText = new QString("Could not open the file from disk!n");
msgText->append(err);
QString *msgTitle = new QString("ERROR: Could not open the file!");
emit errMsg(msgTitle, msgText, "WARNING");
delete file;
return;
}
QDataStream out(file);
QString line = "MyApp";
out << line;
line.setNum(size);//size = 10
out << line;
line.setNum(sizeof(double));
out << line;
for(int i = 0; i < size; i++)
{
out << array[i];
}
if(out.status() != QDataStream::Ok)
{
qCritical("error: " + QString::number(out.status()).toAscii());
}
file->close();
delete file;
}
void FileString::ReadBinary()
{
bool ok = false;
QString line = "";
QFile *file = new QFile(fileName);
if (!file->open(QFile::ReadOnly))
{
QString err = file->errorString();
QString *msgText = new QString("Could not open the file from disk!n");
msgText->append(err);
QString *msgTitle = new QString("ERROR: Could not open the file!");
emit errMsg(msgTitle, msgText, "WARNING");
delete file;
return;
}
QDataStream in(file);
in >> line;
if(line.simplified().contains("MyApp"))
{
in >> line;
size = line.simplified().toInt();
if(size == 10)
{
int mysize = 0;
in >> line;
mysize = line.simplified().toInt();
if(1)//this block runs perfect
{
for(int i = 0; i < size; i++)
{
in >> array[i];
}
if(in.status() == QDataStream::Ok)
ok = true;
}
}
else if(1)//this block reads only zeros
{
char *reader = new char[mysize + 1];
int read = 0;
double *dptr = NULL;
for(int i = 0; i < size; i++)
{
read = in.readRawData(reader, mysize);
if(read != mysize)
{
break;
}
dptr = reinterpret_cast<double *>(reader);//garbage data stored in dptr, why?
if(dptr)
{
array[i] = *dptr;
dptr = NULL;
}
else
{
break;
}
}

if(in.status() == QDataStream::Ok)
ok = true;
delete[] reader;
}
}
}
if(!ok || (in.status() != QDataStream::Ok))
{
qCritical("error : true" + " status = " + QString::number((int) in.status()).toAscii());
}
file->close();
delete file;
}

编辑:

生成的文件的内容

& M y A p p   1 . 1 8 . 3 . 0    1 0    8?ð      @       @      @      @      @      @      @       @"      @$      

应该包含:

MyApp 1.18.3.010812345678910
"MyApp 1.18.3.0" "10" "8" "12345678910"

如果读取平台上的sizeof double与写入平台上的sizeof double不同,您希望读取什么?

假设您的写作平台上的sizeof double是10。然后,您将一个10字节的序列存储在一个表示10字节双字节的文件中。然后,如果您的读取平台上的sizeof double是8,那么您将尝试将10字节的双字节的位解析为8字节,这显然会以垃圾告终。

下面是一个更直观的int示例:如果你有一个2字节的整数,比如5。如果您将其存储在二进制文件中,您将得到一个2字节的序列:00000000 00000101。然后,如果你试图读取与1字节int相同的数字,你将设法只读取第一个字节,即00000000,结果只得到零。

考虑使用字符串保存替身以便于移植https://stackoverflow.com/a/6790009/817441

请注意,在您的原始代码中,sizeof(double)可以代替硬编码字符串,但它不会迁移到具有不同double大小的不同架构。

顺便说一句,如果你担心双字符串转换的性能,当你的用户或你想稍后转移到嵌入式时,你可能会遇到更多问题。我刚刚循环运行了一些转换,在我的旧笔记本电脑上也没那么糟糕。这是我非常糟糕的基准测试结果:

time ./main 
real    0m1.244s
user    0m1.240s
sys     0m0.000s

我想再次指出,这是一台旧笔记本电脑。

对于代码:

#include <QString>
int main()
{
for (int i = 0; i < 1000000; ++i)
QString::number(5.123456789012345, 'g', 15);
return 0;
}

因此,我建议使用以下方法,而不是不可移植的直接写入:

QString QString::数字(双n,字符格式='g',int精度=6)[static]

返回一个相当于数字n的字符串,根据指定的格式和精度进行格式化。有关详细信息,请参见参数格式。

与QLocale::toString()不同,此函数不支持用户的区域设置。

http://doc-snapshot.qt-project.org/qdoc/qstring.html#number-2

在讨论了所有这些之后,如果我是你,我会写这样的东西:

void FileString::SaveBinary()
{
QFile *file = new QFile(fileName);
if (!file->open(QFile::WriteOnly))
{
QString err = file->errorString();
QString *msgText = new QString("Could not open the file from disk!n");
msgText->append(err);
QString *msgTitle = new QString("ERROR: Could not open the file!");
emit errMsg(msgTitle, msgText, "WARNING");
delete file;
return;
}
QDataStream out(file);
QString line = QString::number(myDouble);
out << line;
for(int i = 0; i < size; i++)
{
out << array[i];
}
if(out.status() != QDataStream::Ok)
{
qCritical("error: " + QString::number(out.status()).toAscii());
}
file->close();
delete file;
}

一个可移植的选项可以是使用长double,但这当然会增加其他地方的计算量,因此根据场景的不同,它可能是一个选项,也可能不是一个选项。