在C++中保存双二进制的问题

在C++中保存双二进制的问题,c++,fstream,binaryfiles,C++,Fstream,Binaryfiles,在我的粒子系统模拟代码中,我为粒子定义了一个类,每个粒子都有一个包含其位置的pos属性,即双pos[3];因为每个粒子有3个坐标分量。因此,使用粒子定义的粒子对象=新粒子[npart];由于我们有很多粒子,例如,第二个粒子的y分量将通过双dummycamp=particles[1].pos[1]访问 要在使用二进制之前将粒子保存到文件,我将使用另存为txt,浮点精度为10,每行一个粒子: #include <iostream> #include <fstream> of

在我的粒子系统模拟代码中,我为粒子定义了一个类,每个粒子都有一个包含其位置的pos属性,即双pos[3];因为每个粒子有3个坐标分量。因此,使用粒子定义的粒子对象=新粒子[npart];由于我们有很多粒子,例如,第二个粒子的y分量将通过双dummycamp=particles[1].pos[1]访问

要在使用二进制之前将粒子保存到文件,我将使用另存为txt,浮点精度为10,每行一个粒子:

#include <iostream>
#include <fstream>

ofstream outfile("testConfig.txt", ios::out);
outfile.precision(10);

  for (int i=0; i<npart; i++){
    outfile << particle[i].pos[0] << " " << particle[i].pos[1]  << " " << particle[i].pos[2] << endl;
}
outfile.close();
为了将粒子位置保存为二进制,将上述样本的保存部分替换为

  ofstream outfile("test.bin", ios::binary | ios::out);

  for (int i=0; i<npart; i++){
  outfile.write(reinterpret_cast<const char*>(particles[i].pos),streamsize(3*sizeof(double))); 
  }
  outfile.close();
但是考虑到评论中对二进制格式的不可移植性的质疑,我不相信Python中的这种阅读方式会一直有效!如果有人能说明这种方法的可靠性,那就太好了。

我建议使用库,而不是从头开始编写序列化/反序列化例程。我发现真的很容易使用,甚至比以前更容易。它减少了在您自己的代码中出现错误的机会

在你的情况下,我会用麦片连续制作这样的双打:

包括 包括 int main{ std::ofstream outfiletest.bin,ios::binary; 谷物::二进制输出文件; 双x,y,z; x=y=z=42.0; outx,y,z; } 要反序列化它们,请使用:

包括 包括 int main{ std::ifstream infiltetest.bin,ios::binary; 谷类:二元多糖; 双x,y,z; inx,y,z; } 您还可以以相同的方式序列化/反序列化整个std::Vector。只需在单个std::vector上添加include并使用in/out,就像在给定示例中一样,而不是在多个double上

太棒了

编辑

在您询问的一条评论中,是否可以用Python读取这样创建的二进制文件

答复:

序列化的二进制文件实际上并不意味着具有很强的可移植性,像endianness这样的东西可能在这里发挥作用。您可以轻松地修改我给您的示例代码来编写文件,这是使用库和数据库的另一个优点

我建议使用库,而不是从头开始编写序列化/反序列化例程。我发现真的很容易使用,甚至比以前更容易。它减少了在您自己的代码中出现错误的机会

在你的情况下,我会用麦片连续制作这样的双打:

包括 包括 int main{ std::ofstream outfiletest.bin,ios::binary; 谷物::二进制输出文件; 双x,y,z; x=y=z=42.0; outx,y,z; } 要反序列化它们,请使用:

包括 包括 int main{ std::ifstream infiltetest.bin,ios::binary; 谷类:二元多糖; 双x,y,z; inx,y,z; } 您还可以以相同的方式序列化/反序列化整个std::Vector。只需在单个std::vector上添加include并使用in/out,就像在给定示例中一样,而不是在多个double上

太棒了

编辑

在您询问的一条评论中,是否可以用Python读取这样创建的二进制文件

答复:

序列化的二进制文件实际上并不意味着具有很强的可移植性,像endianness这样的东西可能在这里发挥作用。您可以轻松地修改我给您的示例代码来编写文件,这是使用库和数据库的另一个优点


Oh and Groove::JSONOutputArchive有一个。

问题在于ascii中double的10进制表示有缺陷,不能保证给出正确的结果,特别是当您仅使用10位数字时。即使您使用所有std::numeric_limits数字,也可能会丢失信息,因为数字可能无法准确表示在10进制中

另一个问题是double的二进制表示没有标准化,因此使用它非常脆弱,并且很容易导致代码中断。简单地更改编译器或编译器的位置可能会导致不同的双重格式,而更改体系结构则完全无法保证

您可以使用双精度十六进制格式将其序列化为非有损表示的文本

 stream << std::fixed << std::scientific << particles[i].pos[0];

 // If you are using C++11 this was simplified to

 stream << std::hexfloat << particles[i].pos[0];
这会影响打印与C中printf中的%a相同的值,该值将字符串打印为十六进制浮点,小写。在这里,基数和尾数都转换为十六进制值,然后以非常特定的格式打印。由于底层表示是二进制的,所以这些值可以用十六进制精确表示,并提供一种在系统之间传输数据的无损耗方式。它还截断了进行中的零和后续的零,因此对于许多数字来说是相对紧凑的

在python方面。也支持此格式。您应该能够读取字符串形式的值,然后使用float.fromhex将其转换为浮点

请参阅:

但您的目标是节省空间:

但是现在,为了节省空间,我尝试将配置保存为二进制文件

我想问一个问题,你真的需要节省空间吗?您是否在低能耗、低资源的环境中运行?当然,节省空间肯定是一件事,但这在当今是罕见的,但这些环境确实存在


但看起来你正在运行某种形式的粒子模拟。这并不需要低资源用例。即使你有万亿字节的数据,我仍然会选择一种可移植的易于阅读的二进制格式。最好是没有损耗的。存储空间很便宜。

问题在于ascii中double的基数10表示法有缺陷,不能保证给出正确的结果,特别是当您仅使用10位数字时。即使您使用所有std::numeric_limits数字,也可能会丢失信息,因为数字可能无法准确表示在10进制中

另一个问题是double的二进制表示没有标准化,因此使用它非常脆弱,并且很容易导致代码中断。简单地更改编译器或编译器的位置可能会导致不同的双重格式,而更改体系结构则完全无法保证

您可以使用双精度十六进制格式将其序列化为非有损表示的文本

 stream << std::fixed << std::scientific << particles[i].pos[0];

 // If you are using C++11 this was simplified to

 stream << std::hexfloat << particles[i].pos[0];
这会影响打印与C中printf中的%a相同的值,该值将字符串打印为十六进制浮点,小写。在这里,基数和尾数都转换为十六进制值,然后以非常特定的格式打印。由于底层表示是二进制的,所以这些值可以用十六进制精确表示,并提供一种在系统之间传输数据的无损耗方式。它还截断了进行中的零和后续的零,因此对于许多数字来说是相对紧凑的

在python方面。也支持此格式。您应该能够读取字符串形式的值,然后使用float.fromhex将其转换为浮点

见:

但您的目标是节省空间:

但是现在,为了节省空间,我尝试将配置保存为二进制文件

我想问一个问题,你真的需要节省空间吗?您是否在低能耗、低资源的环境中运行?当然,节省空间肯定是一件事,但这在当今是罕见的,但这些环境确实存在


但看起来你正在运行某种形式的粒子模拟。这并不需要低资源用例。即使你有万亿字节的数据,我仍然会选择一种可移植的易于阅读的二进制格式。最好是没有损耗的。存储空间很便宜。

如果你曾经研究过将数据转换为矢量坐标而不是笛卡尔X,Y,Z坐标的想法,你会感到好奇吗?这似乎可能会将数据的大小减少约30%:两个坐标而不是三个坐标,但可能需要稍高的精度才能转换回X、Y、Z


矢量坐标仍然可以通过使用文本压缩或二进制转换以上的各种压缩技术进行进一步优化。

您是否曾研究过将数据转换为矢量坐标而不是笛卡尔X,Y,Z的想法?这似乎可能会将数据的大小减少约30%:两个坐标而不是三个坐标,但可能需要稍高的精度才能转换回X、Y、Z


矢量坐标仍然可以通过使用文本压缩或二进制转换以上的各种压缩技术进行进一步优化。

旁注:使用ifstream和ofstream时,无需指定方向;i表示输入,o表示输出,你几乎不需要显式地关闭它们中的任何一个——析构函数会处理这个问题。请不要重新发明轮子。使用sane库进行序列化,如或。pos的定义是什么?是[3]吗?因此,对于由粒子定义的粒子对象=新粒子[npart];-哪里评论和描述不是事实,只有代码才是事实。请发布一个。当你需要重新解释时,你需要停下来想想我在写错误,答案通常是肯定的。旁注:使用ifstream和ofstream时,你不需要指定方向;i表示输入,o表示输出,你几乎不需要显式地关闭它们中的任何一个——析构函数会处理这个问题。请不要重新发明轮子。使用sane库进行序列化,如或。pos的定义是什么?是[3]吗?因此,对于由粒子定义的粒子对象=新粒子[npart];-哪里评论和描述不是事实,只有代码才是事实。请发布一个。当你需要重新解释施法时,你需要停下来想想我在这里写错误,答案通常是肯定的。你可能需要trunc和out标志来进行排水
e和in表示填充。记住:/。没有超出C++标准库所需的额外库。@ MeimeGeRoSukIfIfSuffin默认为OFFROW。@ JesperJuhl,我推断它最好推荐一个完整的解决方案。哦,麦片粥只是其中的一小部分。@drescherjm你没有错,你是对的。XML或JSON将更大,但可移植。二进制文件将更小,但不可移植。折衷方案是A或压缩的XML/JSON。outfile可能需要trunc和out标志,infile可能需要in标志。记住:/。没有超出C++标准库所需的额外库。@ MeimeGeRoSukIfIfSuffin默认为OFFROW。@ JesperJuhl,我推断它最好推荐一个完整的解决方案。哦,麦片粥只是其中的一小部分。@drescherjm你没有错,你是对的。XML或JSON将更大,但可移植。二进制文件将更小,但不可移植。折衷方案是使用压缩的XML/JSON或XML/JSON。非常感谢所有的解释,这真的很有帮助!使用hexfloat真是个有趣的主意!我现在要试一试!嗨,Martin,我无法使用hexfloat写入文件,有什么诀窍吗?在文件外。写。。。在上面的代码片段中。谢谢你的提示。@user929304 outfile非常感谢你的解释,这真的很有帮助!使用hexfloat真是个有趣的主意!我现在要试一试!嗨,Martin,我无法使用hexfloat写入文件,有什么诀窍吗?在文件外。写。。。在上面的代码片段中。谢谢您对此的任何提示。@user929304 outfile
data = np.fromfile('test.bin', dtype=np.float64)
data
array([-74.04119568, -44.33692582,  17.36278231,  48.16310086,
       -65.02325252, -37.2053818 ])
 stream << std::fixed << std::scientific << particles[i].pos[0];

 // If you are using C++11 this was simplified to

 stream << std::hexfloat << particles[i].pos[0];