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

时间:2014-01-11 09:59:06

标签: c++ qt

我正在编写一个二进制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"

2 个答案:

答案 0 :(得分:1)

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

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

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

考虑使用字符串来保存可移植性的双精度https://stackoverflow.com/a/6790009/817441

答案 1 :(得分:1)

请注意,在原始代码中,sizeof(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 :: number(double n,char format ='g',int precision = 6)[静态]

     

返回等价于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;
}

一个便携式选项可能是使用long double,但当然这会增加其他地方的计算,因此根据场景,它可能是也可能不是一种选择。