使用 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
,即使不使用<<
运算符也是如此。
【讨论】:
我在我的 linux 机器上尝试过,但无法重现结果。两个文件都包含预期的 4 个字节,没有额外的字节。我知道这在某种程度上与 Windows 系统上的特定文本格式有关,但是如果你忘记了ios::binary
,你可能会在输出中得到随机的东西,这是非常可怕的。
@DarioP:我确认我在测试时使用的是 Windows【参考方案3】:
文本流与二进制流的含义因平台而异,有些不可预测。
但就流行平台而言,这很简单:在 Linux 和 MacOS X 上,没有区别。在 Windows 上,唯一的区别是内部\n
被转换为外部流中的\r\n
。
【讨论】:
在 Windows 下,0x1A 将被视为文本模式的文件结尾。以上是关于使用 std::ios_base::binary 有啥意义?的主要内容,如果未能解决你的问题,请参考以下文章
在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?