使用 std::ios_base::binary 有啥意义?

Posted

技术标签:

【中文标题】使用 std::ios_base::binary 有啥意义?【英文标题】:What the point of using std::ios_base::binary?使用 std::ios_base::binary 有什么意义? 【发布时间】:2021-05-22 07:50:20 【问题描述】:

我在 Window 下读取 Linux 文件时遇到问题。以下是问题讨论:Using fstream::seekg under windows on a file created under Unix。

通过打开指定了std::ios_base::binary文本文件解决了这个问题。

但是这种模式的实际意义是什么?如果指定,您仍然可以将文件作为文本文件处理(使用mystream << "Hello World" << std::endl 写入,使用std::getline 读取)。

在 Windows 下,我注意到的唯一区别是 mystream << "Hello World" << std::endl 使用:

如果未指定 std::ios_base::binary,则 0x0D 0x0A 作为行分隔符(EOL 和回车) 如果指定了 std::ios_base::binary,则 0x0A 作为行分隔符(仅限 EOL)

打开使用std::ios_base::binary 生成的文件时,记事本不会智能地显示行。 vi 或 Wordpad 等更好的编辑器会显示它们。

这真的是使用std::ios_base::binary 和不使用std::ios_base::binary 生成的文件之间的唯一区别吗?文档说Consider stream as binary rather than text.,这到底是什么意思?

如果我不关心在记事本中打开文件并希望fstream::seekg 始终工作,那么始终设置std::ios_base::binary 是否安全?

【问题讨论】:

我也会在字符串中使用\0 字符进行测试。在二进制中,这些可能只是输出,而对于非二进制,它们可能被解释为字符串终止符。 @GáborBakos 如果唯一涉及的系统是 Unix 和 Windows,那么唯一的区别是行尾和 0x1A,Windows 将其视为文件结尾字符,至少在输入时。 【参考方案1】:

二进制和文本模式的区别在于实现 已定义,但仅涉及最低级别:它们不会更改 <<>> 之类的东西的含义(插入和提取文本 数据)。此外,正式地,输出除少数不可打印之外的所有内容 如果文件是文本,字符(如'\n')是未定义的行为 模式。

对于最常见的操作系统:在Unix下,没有区别;两者都是 完全相同的。 Windows下,'\n'内部会映射到这两个 字符序列 CR, LF (0x0D, 0x0A) 外部和 0x1A 将 读取时解释为文件结尾。在更具异国情调(而且主要是 绝种)操作系统,但是,它们可以用完全不同的方式表示 操作系统级别的文件类型,并且可能无法读取文件 如果它是以二进制模式编写的,则为文本模式,反之亦然。或者你 可能会看到不同的东西:行尾有额外的空白,或者 二进制模式下没有'\n'

关于始终设置std::ios_base::binary:我的政策 可移植文件是要确切地决定我希望它们如何格式化,设置 二进制,并输出我想要的。这通常是CR,LF,而不仅仅是 LF,因为那是网络标准。另一方面,大多数 Windows程序只用LF没有问题,但我遇到过 不少 Unix 程序在 CR、LF 上有问题;哪一个 主张系统地只使用 LF(这也更容易)。正在做 这种方式意味着我得到相同的结果,无论是否 我在 Unix 或 Windows 下运行。

【讨论】:

我是否明白设置std::ios_base::binary 或不设置文件读取 没有区别(除了修复上面提到的错误)和设置std::ios_base::binary 或不设置文件写法可能会导致基于平台的差异? @jpo38 不可以。二进制和文本之间的选择会影响读写:在Windows下,读取时,CR,LF会映射到LF,0x1A会导致读取停止.在某些特殊系统上,如果文件是用文本编写的,则以二进制模式打开可能会失败,反之亦然。 @JamesKanze -- 我认为其他人已经评论说(至少在 MacOS 上)如果你设置了流操作符 IGNORE 二进制模式:这意味着如果你使用“>>”(提取格式化操作符) 从流中读取二进制数据,您将看到 CR、LF 扩展/转换,即使您可能没有预料到二进制模式会出现这种情况!我追查了因使用“>>”而引入的复杂二进制文件格式错误。使用普通的 read() 可以轻松解决这些问题。【参考方案2】:

我发现(通过失去两个小时的工作试图了解发生了什么)指定std::ios_base::binary 确实会产生巨大的影响。

std::vector<char> data 0x01, 0x02, 0x0A, 0x0B ;

    std::fstream tfat;
    tfat.open( "binary", std::ios_base::out | std::ios_base::binary );
    tfat.write( &(data[0]), data.size() );
    tfat.close();


    std::fstream tfat;
    tfat.open( "not_binary", std::ios_base::out );
    tfat.write( &(data[0]), data.size() );
    tfat.close();

然后,“二进制”文件包含 4 个字节:0x01, 0x02, 0x0A, 0x0B 但“not_binary”文件包含 5 个字节:0x01, 0x02, 0x0D, 0x0A, 0x0B

0x0D (\r) 被插入到 0x0A (\n) 之前。虽然我写了 4 个字节,但我希望文件中最后有 4 个字节。

所以这让我意识到为什么在将数据写入文件时必须使用std::ios_base::binary,即使不使用&lt;&lt; 运算符也是如此。

【讨论】:

我在我的 linux 机器上尝试过,但无法重现结果。两个文件都包含预期的 4 个字节,没有额外的字节。我知道这在某种程度上与 Windows 系统上的特定文本格式有关,但是如果你忘记了 ios::binary,你可能会在输出中得到随机的东西,这是非常可怕的。 @DarioP:我确认我在测试时使用的是 Windows【参考方案3】:

文本流与二进制流的含义因平台而异,有些不可预测。

但就流行平台而言,这很简单:在 Linux 和 MacOS X 上,没有区别。在 Windows 上,唯一的区别是内部\n 被转换为外部流中的\r\n

【讨论】:

在 Windows 下,0x1A 将被视为文本模式的文件结尾。

以上是关于使用 std::ios_base::binary 有啥意义?的主要内容,如果未能解决你的问题,请参考以下文章

测试使用

第一篇 用于测试使用

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”