c++ 将数字数据写入文件作为二进制与写出来?

8zzbczxx  于 2023-05-30  发布在  其他
关注(0)|答案(2)|浏览(151)

我正在将浮点数写入文件,但是有两种不同的方式来写入这些数字,我不知道该使用哪种。
这两个选项是:
1.将原始代表位写入文件
1.将数字的ascii表示写入文件
选项1对我来说似乎更实用,因为我将每个浮点数截断为4个字节。并且在阅读时可以完全跳过对每个数字的解析。但在实践中,我只见过使用选项2。
所讨论的数据是3D模型信息,其中小文件大小和快速阅读可能非常有利,但同样,据我所知,没有现有的3D模型格式可以做到这一点,我想这背后一定有一个很好的原因。
我的问题是,有什么理由选择写出数字的形式,而不是位表示?在某些情况下,使用二进制形式会更好吗?

jv4diomz

jv4diomz1#

首先,float在任何架构中都是4个字节,所以当你将内存中的4个字节写入文件时,没有什么是“截断”的。
至于你的主要问题,许多常规文件格式都是为了“互操作性”和易于阅读而设计的。这就是为什么最常使用的文本,它几乎是一种通用的可移植的表示(尽管存在字符编码问题)。
例如,程序很容易从文本文件中读取字符串“123”并知道它代表数字123。
(But注意,文本本身不是格式。您可以选择将所有数据元素表示为ASCII/Unicode/任何字符串,并将所有这些字符串沿着组成一个文本文件,但您仍然需要确切地指定每个元素的含义以及可以在哪里找到哪些数据。例如,一个非常简单的基于文本的3D三角形网格文件格式可能在文件的第一行上有网格中的三角形的数量,然后在接下来的N行上有三个真实的的三元组,每个三元组指定三角形的三个顶点的X、Y、Z坐标所需的9个数字。
另一方面是二进制格式。这些 * 通常 * 在它们中具有与在计算机存储器中发现的格式相同的数据元素。这意味着整数用固定数目的字节表示(1、2、4或8,通常以“二进制补码”格式),或真实的用IEEE 754格式的4或8字节表示。(注意,为了保持重点,我省略了很多细节。
二进制格式的主要优点是:
1.它们通常是较小的尺寸。写为ASCII字符串的32位整数最多可以得到10或11个字节(例如-100000000),但在二进制中,它总是占用4个字节。更小意味着更快的传输(通过网络,从磁盘到内存等)和更容易存储。
1.每个数据元素的读取速度更快。不需要复杂的解析。如果数据元素恰好是您的平台/语言可以使用的格式/布局,那么您只需要将少量字节从磁盘传输到内存,就完成了。
1.即使是大型而复杂的数据结构也可以以与存储器中相同的方式在磁盘上进行布局,然后您要“读取”这种格式,只需简单快速地将大量字节(可能包含许多数据元素)从磁盘中放入存储器,就完成了。
但是第三个优点要求您将磁盘上的数据布局 * 精确 *(逐位)与内存中的数据结构布局匹配。这意味着,几乎总是,该文件格式只适用于您的代码,并且仅适用于您的代码,即使您在自己的代码中更改了一些内容也不适用。这意味着它根本不是可移植的或可互操作的。但它是该死的快与工作!
二进制格式也有缺点:
1.你再也不能在简单的通用软件(如文本编辑器)中查看、编辑或理解它们了。您可以在任何文本编辑器中打开任何XML、JSON或配置文件,并很容易地理解它们,但不是JPEG文件。
1.你通常需要更具体的代码来读写二进制格式,而不是文本格式。更不用说说明文件中每一位应该是什么了。文本文件通常更容易解释和明显。
1.在一些(许多)语言(脚本和“高级”语言)中,你通常无法访问组成整数或浮点数的字节,无法读取或写入它们。这意味着,当您使用C或C++这样的低级语言时,您将失去二进制文件给予的大部分速度优势。
1.基本数据类型的二进制内存格式几乎总是与内存所连接的硬件(或更一般地说,整个平台)相关联。当您选择将相同的位从内存写入文件时,文件格式也会变得依赖于硬件。一个硬件存储浮点真实的的方式可能与另一个硬件不完全相同,这意味着写在一个硬件上的二进制文件不能在另一个硬件上简单地读取(必须小心,并小心地将数据转换成目标格式)。硬件架构之间的一个主要区别是“端序”,它影响了多字节原语(例如:4字节整数或8字节浮点数)预期存储在存储器中(从最高阶字节到最低阶,或反之亦然,其分别被称为“大端序”和“小端序”。PowerPC)并在little-endian架构上逐字读取(例如x86)将使每个原语中的所有字节从高值交换到低值,这意味着所有(好吧,几乎所有)值都是错误的。

既然你提到了3D模型数据,让我给予你一个典型游戏引擎中使用的格式的例子。游戏引擎运行时很可能需要最快的速度来阅读模型,而3D模型很大,所以通常它的模型文件有一个非常特定的、完全不可移植的格式。但这种格式很可能不受任何建模软件的支持。因此,您需要编写一个转换器(也称为导出器或导入器),该转换器将采用常见的通用格式(例如,OBJ、DAE等),并将其转换为特定于引擎的专有格式。但是正如我提到的,阅读/传输/使用基于文本的格式比二进制格式更容易,所以你通常会选择基于文本的通用格式来导出你的模型,然后在它们上运行转换器,以优化,二进制,引擎特定的运行时格式。

ikfrs5lh

ikfrs5lh2#

在以下情况下,您可能更喜欢二进制格式:

  • 您需要更紧凑的编码(更少的字节-因为文本编码可能会占用更多的空间)。
  • 精度-因为如果你编码为文本,你可能会失去精度-但也许有办法编码为文本而不失去精度 *。
  • 性能可能也是二进制编码的另一个优点。

由于您提到的数据是3D模型模拟,因此编码的紧凑性(也可能是性能)和精度可能与您相关。另一方面,文本编码是人类可读的。
也就是说,使用二进制编码,您通常会遇到像endianness这样的问题,并且浮点数表示在不同的机器上可能会有所不同,但here是一种以可移植的方式以二进制格式编码浮点数(或双精度)的方法:

uint64_t pack754(long double f, unsigned bits, unsigned expbits)
{
    long double fnorm;
    int shift;
    long long sign, exp, significand;
    unsigned significandbits = bits - expbits - 1; // -1 for sign bit

    if (f == 0.0) return 0; // get this special case out of the way

    // check sign and begin normalization
    if (f < 0) { sign = 1; fnorm = -f; }
    else { sign = 0; fnorm = f; }

    // get the normalized form of f and track the exponent
    shift = 0;
    while(fnorm >= 2.0) { fnorm /= 2.0; shift++; }
    while(fnorm < 1.0) { fnorm *= 2.0; shift--; }
    fnorm = fnorm - 1.0;

    // calculate the binary form (non-float) of the significand data
    significand = fnorm * ((1LL<<significandbits) + 0.5f);

    // get the biased exponent
    exp = shift + ((1<<(expbits-1)) - 1); // shift + bias

    // return the final answer
    return (sign<<(bits-1)) | (exp<<(bits-expbits-1)) | significand;
}
  • :在C中,由于C99有seems的方式来做到这一点,但我仍然认为这将需要更多的空间。

相关问题