【问题标题】:Writing numerical data to file as binary vs. written out?将数字数据作为二进制文件写入文件与写出?
【发布时间】:2015-07-11 22:51:31
【问题描述】:

我正在将浮点数写入文件,但有两种不同的方式来写入这些数字,我想知道使用哪种方式。

两个选择是:

  1. 将原始代表位写入文件
  2. 将数字的 ascii 表示形式写入文件

选项 1 对我来说似乎更实用,因为我将每个浮点数截断为 4 个字节。并且在阅读时可以完全跳过解析每个数字。但在实践中,我只见过使用选项 2。

有问题的数据是 3D 模型信息,其中小文件大小和快速读取可能非常有利,但同样,据我所知,没有现有的 3D 模型格式可以做到这一点,我想这背后一定有充分的理由.

我的问题是,有什么理由选择写出数字的形式而不是位表示?有没有更喜欢使用二进制形式的情况?

【问题讨论】:

  • 不确定这是否是主要原因,但一个原因是您不必担心机器之间的endianness 不同。
  • 以二进制形式编写以供计算机高效使用,以文本形式编写以供人类高效使用(例如,调试)。
  • as a floating point number will always consume a fixed number of bytes, and only 4 at that. 错误。更糟糕的是,尺寸只是众多问题之一。
  • @deviantfan - 抱歉,我不应该像一般情况下那样写。但在我的情况下,它总是写为 4 个字节,截断任何额外的精度

标签: c++ file 3d numbers file-writing


【解决方案1】:

首先,floats 在您通常可能遇到的任何架构上都是 4 个字节,因此当您将 4 个字节从内存写入文件时,不会“截断”任何内容。

至于您的主要问题,许多常规文件格式都是为“互操作性”和易于阅读/写作而设计的。这就是为什么最常使用文本这种几乎普遍可移植的表示(尽管存在字符编码问题)的原因。

例如,程序很容易从文本文件中读取字符串“123”并知道它代表数字 123。

(但请注意,文本本身并不是一种格式。您可以选择将所有数据元素表示为 ASCII/Unicode/任何字符串,并将所有这些字符串彼此放在一起形成一个文本文件,但是您仍然需要准确指定每个元素的含义以及可以在哪里找到哪些数据。例如,一个非常简单的基于文本的 3D 三角形网格文件格式可能在文件的第一行包含网格中的三角形数量,然后是在接下来的 N 行中,三个实数三元组,每行指定三角形三个顶点的 X、Y、Z 坐标所需的 9 个数字。)

另一方面是二进制格式。这些通常包含与计算机内存中相同格式的数据元素。这意味着整数用固定数量的字节表示(1、2、4 或 8,通常采用“二进制补码”格式)或实数用 IEEE 754 格式的 4 或 8 个字节表示。 (请注意,为了保持重点,我省略了很多细节。)

二进制格式的主要优点是:

  1. 它们的尺寸通常较小。写成 ASCII 字符串的 32 位整数最多可以占用 10 或 11 个字节(例如 -1000000000),但在二进制中它总是占用 4 个字节。更小意味着传输速度更快(通过网络、从磁盘到内存等)并且更易于存储。

  2. 每个数据元素的读取速度更快。不需要复杂的解析。如果数据元素恰好是您的平台/语言可以使用的确切格式/布局,那么您只需将几个字节从磁盘传输到内存即可。

  3. 即使是大而复杂的数据结构也可以像在内存中一样以完全相同的方式在磁盘上布置,然后您需要做的就是“读取”它格式就是将大块字节(可能包含许多数据元素)从磁盘获取到内存中,通过一个简单快速的操作,您就完成了。

但第三个优势要求您将磁盘上的数据布局完全(逐位)与内存中数据结构的布局相匹配。这意味着,几乎总是,该文件格式仅适用于您的代码并且仅适用于您的代码,即使您在自己的代码中更改了一些内容也不行。这意味着它根本不是可移植的或可互操作的。但它的工作速度非常快!

二进制格式也有缺点:

  1. 您无法再在文本编辑器等简单的通用软件中查看、编辑或理解它们。您可以在任何文本编辑器中打开任何 XML、JSON 或配置文件并很容易理解它,但不是 JPEG 文件。

  2. 您通常需要更具体的代码来读入/写出二进制格式,而不是文本格式。更不用说记录文件的每一位应该是什么的规范了。文本文件通常更加不言自明。

  3. 在某些(许多)语言(脚本和“高级”语言)中,您通常无权访问构成整数或浮点数的字节,无法读取或写入它们。这意味着当您使用 C 或 C++ 等低级语言工作时,您将失去二进制文件为您提供的大部分速度优势。

  4. 原始数据类型的二进制内存格式几乎总是与内存所连接的硬件(或更一般地说,整个平台)相关联。当您选择将相同的位从内存写入文件时,文件格式也取决于硬件。一种硬件可能不会以与另一种硬件完全相同的方式存储浮点实数,这意味着写在一个硬件上的二进制文件不能在另一个上天真地读取(必须小心,数据必须小心地转换为目标格式。)一个主要区别硬件架构之间的差异被称为“字节顺序”,它影响多字节原语(例如 4 字节整数或 8 字节浮点数)如何存储在内存中(从最高字节到最低字节,反之亦然反之亦然,分别称为“大端”和“小端”。)在大端架构(例如 PowerPC)上写入二进制文件并在小端架构(例如 x86)上逐字读取的数据将具有所有每个原语中的字节从高值交换到低值,这意味着所有(嗯,几乎所有)值都是错误的。

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

【讨论】:

  • 字节序呢?举一个例子,我在 Big Endian 平台上将 32 位值写入文件,然后在 Little Endian 平台上读回。 Little Endian 平台上的数字会不会一样?
  • @ThomasMatthews:不会是一样的,除非这个数字碰巧是一个字节回文(例如 0x12343412。)
【解决方案2】:

如果满足以下条件,您可能更喜欢二进制格式:

  • 您想要更紧凑的编码(更少的字节 - 因为文本编码可能会占用更多空间)。
  • 精度 - 因为如果您编码为文本,您可能会丢失精度 - 但也许有一些方法可以在不丢失精度的情况下编码为文本*。
  • 性能可能也是二进制编码的另一个优势。

由于您提到有问题的数据是 3D 模型模拟,因此编码的紧凑性(也许还有性能)和精度可能与您相关。另一方面,文本编码是人类可读的。

也就是说,使用二进制编码时,您通常会遇到字节顺序等问题,并且浮点表示在不同的机器上可能会有所不同,但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 有一种方法可以做到这一点,但我仍然认为它会占用更多空间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 2016-01-15
    • 2010-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-08
    相关资源
    最近更新 更多