【问题标题】:What's the difference between ifstream with or without istreambuf_iterator?带或不带 istreambuf_iterator 的 ifstream 有什么区别?
【发布时间】:2015-03-12 16:52:08
【问题描述】:

我需要读取一个包含标题和数据的二进制文件(一次性)。用 C++ 读取文件有不同的方法,我想知道哪种方法最快、更可靠。我也不知道reintrerpret_cast 是否是将原始数据转换为结构的最佳方式。

编辑:标题结构没有任何功能,只有数据。

ifstream File(Filename, ios::binary);    // Opens file

if (!File)    // Stops if an error occured
{
    /* ... */
}

File.seekg(0, ios::end);
size_t Size = File.tellg();    // Get size
File.seekg(0, ios::beg);

这是没有 istreambuf_iterator 的 ifstream

char* Data = new char[Size];

File.read(Data, Size);
File.close();

HeaderType *header = reinterpret_cast<HeaderType*>(Data);

/* ... */

delete[] Data;

这是带有 istreambuf_iterator 的 ifstream

std::string Data;    // Is it better to use another container type?

Data.reserve(Size);
std::copy((std::istreambuf_iterator<char>(File)), std::istreambuf_iterator<char>(),
          std::back_inserter(Data));

File.close();

const HeaderType *header = reinterpret_cast<HeaderType*>(Data.data());

在网上也找到了这个

std::ostringstream Data;
Data << File.rdbuf();
File.close();
std::string String = Data.str();

const HeaderType *header = reinterpret_cast<HeaderType*>(String.data());

【问题讨论】:

  • 当人们将 PascalCase 用于 C++ 变量名时,还有其他人会做双重考虑吗?

标签: c++ ifstream istream-iterator


【解决方案1】:

将文件内容读入char*,然后执行reinterpret_castHeaderType* 不是一个好主意。

来自标准:

5.2.10 重新解释演员表

...

7 对象指针可以显式转换为不同类型的对象指针70。当类型为“指向T1”的纯右值v 转换为类型“指向cv T2”的指针时,如果T1 和@987654330 都存在,则结果为static_cast&lt;cv T2*&gt;(static_cast&lt;cv void*&gt;(v)) @ 是标准布局类型 (3.9),T2 的对齐要求不比 T1 严格,或者如果任一类型为 void。将“指向T1的指针”类型的纯右值转换为“指向T2的指针”类型(其中T1T2是对象类型,T2的对齐要求并不比@更严格987654339@) 并返回其原始类型会产生原始指针值。未指定任何其他此类指针转换的结果。

在您的情况下,如果 HeaderType 的对齐要求比 char 更严格,您将遇到未定义的行为。

如果你有选择,我会建议。

  1. 先阅读标题。

    HeaderType header;
    File.read(reinterpret_cast<char*>(&header), sizeof(HeaderType));
    
  2. 根据header的值读取其余数据。

【讨论】:

  • 你的意思是“如果结构有对齐的变量”(或本身对齐)?结构未对齐。多次调用File.read 是否比一次阅读要慢很多?
  • @LHLaurini,我没有关注你关于对齐的问题。您可以在en.cppreference.com/w/cpp/language/object#Alignment 阅读有关它的更多信息。关于多次调用File.read(),速度较慢,但​​我不知道它是否重要。
  • @R Sahu 谢谢,但我知道对齐是什么。
  • @LHLaurini,这并不意味着轻微。关于对齐问题,是类的对齐要求,而不是类的变量的对齐要求。
  • @R Sahu 对不起,我有时有点(或很多)愚蠢。我想我现在明白了。 (我稍后可能会打扰你)
【解决方案2】:

这将是“基于意见的”,因此对于 SO 来说并不是严格意义上的 on-tpoic。

但是我看不出在这种情况下使用迭代器有什么意义,因为read() 函数更简洁。

但是,更重要的是,您执行此操作的方式违反了严格的别名规则,因为您的 struct 内存中的对齐方式不能保证与 char 数组一致。

最好将struct 的地址转换为char*,而不是反过来:

HeaderType header;

File.read(reinterpret_cast<char*>(&header), sizeof(header));
File.close();

像这样读取二进制数据是不可移植的,并且不适用于复杂的用户定义类型(如std::string),因此最好将所有数据成员序列化为格式化字符串。

注意:有关类型别名的信息,请参阅reinterpret_cast 的文档。

【讨论】:

  • 我不知道...如果我使用File.read(reinterpret_cast&lt;char*&gt;(&amp;header), sizeof(header));,我需要进行多次阅读,不是吗? (文件中不仅有标题)但是读取标题和读取所有数据之间真的有很大区别,然后分配一个指向结构所在位置的指针吗?
  • @LHLaurini 您有责任确保您读入的数据与您分配给结构的内存正确对齐。你如何做到这一点取决于你的数据是如何存储的,以及可移植性是否是一个问题(例如,如果数据来自网络)。有关别名规则的解释,请参见 R Sahu 的回答。
  • 我只使用reinterpret_cast&lt;HeaderType&gt;(),因为我认为只要没有对齐的数据,它就是安全的。我知道,如果有对齐的数据,我需要处理它。我错了吗?
  • @LHLaurini 您的HeaderType 是否与char 共享对齐取决于实现,它可能会很好地工作。 但是没有说它不会在您升级编译器时停止工作,或者即使您开始使用不同的标志进行编译。
  • 好的,谢谢您的回答。我知道打包,但是如果我将原始数据转换为结构,并且它们具有不同的对齐方式,会发生什么? (编辑:编译器没有给出任何警告——它被设置为显示任何警告)
【解决方案3】:

首先,您描述的任何解决方案都不会真正起作用;这 reinterpret_cast 应该告诉你。在某些时候,你必须 解析缓冲区中的字节,并插入提取的数据字段 字段到您的内部数据结构中。

关于尽快将字节放入缓冲区, 你做的额外工作越少越好。最快的方法是 使用低级 IO(open 然后在 Unix 下 read),甚至映射 文件到内存中(Unix 下的mmap)。当然,这是系统 依赖;如果你想使用ifstream来实现系统 独立,那么使用istream::read 肯定是最快的(并且 最合乎逻辑的,所有事情都考虑在内)。只要确保流 充满"C" 语言环境,并以二进制模式打开。

声明:使用系统级函数将传输数据 直接从操作系统到您的缓冲区。 istream::read 将从 filebuf 中的内部缓冲区到您的缓冲区中(并使用系统 级别函数将数据放入缓冲区)。另外两个将 逐字节构建std::string 对象,将内存分配为 需要,因为不知道最终长度。

最后,使用std::vector&lt;char&gt;,而不是new char[size]

【讨论】:

  • 感谢您的回答。你能给出一个使用std::vector&lt;char&gt;而不是new char[size]的理由吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多