如何将二进制文件读入无符号字符向量

新手上路,请多包涵

最近有人要求我编写一个函数,将二进制文件读入 std::vector<BYTE> 其中 BYTE 是一个 unsigned char 。很快我就得到了这样的东西:

 #include <fstream>
#include <vector>
typedef unsigned char BYTE;

std::vector<BYTE> readFile(const char* filename)
{
    // open the file:
    std::streampos fileSize;
    std::ifstream file(filename, std::ios::binary);

    // get its size:
    file.seekg(0, std::ios::end);
    fileSize = file.tellg();
    file.seekg(0, std::ios::beg);

    // read the data:
    std::vector<BYTE> fileData(fileSize);
    file.read((char*) &fileData[0], fileSize);
    return fileData;
}

这似乎是不必要的复杂,而且我在调用 file.read 时被迫使用的 char* 的显式转换并没有让我感觉更好。


另一种选择是使用 std::istreambuf_iterator

 std::vector<BYTE> readFile(const char* filename)
{
    // open the file:
    std::ifstream file(filename, std::ios::binary);

    // read the data:
    return std::vector<BYTE>((std::istreambuf_iterator<char>(file)),
                              std::istreambuf_iterator<char>());
}

这是非常简单和简短的,但我仍然必须使用 std::istreambuf_iterator<char> 即使我正在阅读 std::vector<unsigned char>


最后一个似乎非常简单的选项是使用 std::basic_ifstream<BYTE> ,这有点明确表示 “我想要一个输入文件流,我想用它来读取 BYTE s”

 std::vector<BYTE> readFile(const char* filename)
{
    // open the file:
    std::basic_ifstream<BYTE> file(filename, std::ios::binary);

    // read the data:
    return std::vector<BYTE>((std::istreambuf_iterator<BYTE>(file)),
                              std::istreambuf_iterator<BYTE>());
}

但我不确定 basic_ifstream 在这种情况下是否合适。

将二进制文件读入 vector 的最佳方法是什么? 我还想知道 “幕后”发生了 什么以及我可能遇到的可能问题是什么(除了没有正确打开流,这可以通过简单的 is_open 检查来避免)。

有什么好的理由可以在这里使用 std::istreambuf_iterator 吗?

(我能看到的唯一优点是简单)

原文由 LihO 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 869
2 个回答

在测试性能时,我会包括一个测试用例:

 std::vector<BYTE> readFile(const char* filename)
{
    // open the file:
    std::ifstream file(filename, std::ios::binary);

    // Stop eating new lines in binary mode!!!
    file.unsetf(std::ios::skipws);

    // get its size:
    std::streampos fileSize;

    file.seekg(0, std::ios::end);
    fileSize = file.tellg();
    file.seekg(0, std::ios::beg);

    // reserve capacity
    std::vector<BYTE> vec;
    vec.reserve(fileSize);

    // read the data:
    vec.insert(vec.begin(),
               std::istream_iterator<BYTE>(file),
               std::istream_iterator<BYTE>());

    return vec;
}

我的想法是方法 1 的构造函数触及 vector 中的元素,然后 read 再次触及每个元素。

方法 2 和方法 3 看起来最有希望,但可能会遭受一个或多个 resize 的影响。因此,在读取或插入之前要 reserve 的原因。

我还将使用 std::copy 进行测试:

 ...
std::vector<byte> vec;
vec.reserve(fileSize);

std::copy(std::istream_iterator<BYTE>(file),
          std::istream_iterator<BYTE>(),
          std::back_inserter(vec));

最后,我认为最好的解决方案是避免来自 operator >>istream_iterator (以及来自 operator >> 尝试解释二进制数据的所有开销和优点)。但我不知道使用什么可以让您直接将数据复制到向量中。

最后,我对二进制数据的测试显示 ios::binary 没有被兑现。因此 noskipws 来自 <iomanip> 的原因。

原文由 jww 发布,翻译遵循 CC BY-SA 3.0 许可协议

我会认为第一种方法,使用大小并使用 stream::read() 将是最有效的。转换为 char * 的“成本”很可能为零 - 这种类型的转换只是告诉编译器“嘿,我知道你认为这是一种不同的类型,但我真的想要这种类型…… .“,并且不添加任何额外的指令 - 如果您想确认这一点,请尝试将文件读入 char 数组,并比较实际的汇编代码。除了一些额外的工作来确定向量内缓冲区的地址之外,应该没有任何区别。

与往常一样,确定在您的情况下什么是最有效的唯一方法是测量它。 “在网上问”不是证据。

原文由 Mats Petersson 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏