如何读取二进制文件到无符号字符的矢量(How to read a binary file into

2019-07-20 17:38发布

最近我一直在问编写一个函数读取二进制文件到std::vector<BYTE> ,其中BYTEunsigned 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;
}

这似乎是不必要的复杂和显式的char*我是被迫同时呼吁使用file.read不会让我觉得它更好。


另一种选择是使用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的”:

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这里?
(我可以看到的唯一优点是简单)

Answer 1:

当性能测试,我将包括一个测试案例:

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>



Answer 2:

std::ifstream stream("mona-lisa.raw", std::ios::in | std::ios::binary);
std::vector<uint8_t> contents((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());

for(auto i: contents) {
    int value = i;
    std::cout << "data: " << value << std::endl;
}

std::cout << "file size: " << contents.size() << std::endl;


Answer 3:

既然你是加载整个文件到内存最优化的版本,将文件映射到内存中。 这是因为内核加载文件到内核页缓存不管怎样,通过映射文件,你只是暴露在高速缓存中的页面插入过程。 又称零拷贝。

当您使用std::vector<>从内核页面缓存中的数据复制到std::vector<>当你只想读文件,该文件是不必要的。

此外,传递两个输入迭代时std::vector<>在阅读,因为它不知道该文件的大小它生长至其缓冲器。 当调整大小std::vector<>到文件尺寸的第一它不必要地归零出其内容,因为它要与文件数据反正覆盖。 这两种方法都在空间和时间上的次优。



Answer 4:

我本来以为第一种方法,使用的规模和使用stream::read()将是最有效的。 该铸造的“成本” char *是最有可能为零-这种类型转换只是简单地告诉编译器“嘿,我知道你认为这是一个不同的类型,但我真的想在这里这种类型的...”,并执行不添加任何额外的instrucitons - 如果你想证实这一点,尽量把文件读入一个字符数组,比较实际的汇编代码。 除了额外的工作一点点找出矢量内的缓冲区的地址,不应该有任何区别。

与往常一样,唯一的办法肯定告诉你的情况是什么是最有效的是来衡量它。 “问计于互联网上的”是不是证明。



文章来源: How to read a binary file into a vector of unsigned chars