从二进制文件读取/写入十六进制字节

时间:2015-03-12 23:55:04

标签: c++ stl fstream ifstream ofstream

我需要以二进制模式读取文件并将字节作为十六进制值存储在任何STL容器中(最好是std :: list)。后来我需要将它们写回文件,也是二进制模式。所以,我宣布,

typedef unsigned char BYTE;

std::ifstream File("File_Name", std::ios::binary);

std::list<BYTE> File_Bytes;

通过所有的搜索,我理解了一些事情。可以使用std :: istream :: read()或std :: istreambuf_iterator进行读取(我可能非常错误。请纠正我。)而read()函数只将char *作为内存中存储字节的参数输入流的大小。

如果我必须将文件中的字节读入BYTE列表并再次分别使用istream和ostream从BYTE列表写入文件,我将如何执行此操作?请为我澄清一下。感谢。

注意:这实际上是用于霍夫曼编码器/解码器,我需要在程序内部压缩和解压缩,并将解压缩的位写为输出文件。这是为了验证压缩的无损性和程序的正确性。另外,任何人都可以告诉我如何将编码的二进制位写入文件以及编码的Huffman文件具有哪些文件扩展名?非常感谢你。

3 个答案:

答案 0 :(得分:5)

正如注释所阐明的那样,您希望将二进制文件的字节加载到 一些char的STL容器 - 或者更准确地说,uint8_t - 和 将这样的容器保存回二进制文件。

有很多方法可以做到这一点,包括你发现的,使用方法 的std::basic_istream::readstd::basic_ostream::write, 或std::istream_iteratorstd::ostream_iterator

后一种方法产生了最简单的代码。 fread / fwrite方法Container / Container 产生最快的代码,但对于显而易见的更简单更好 将只是你的程序的序幕和结尾操作。

以下是一对匹配的模板函数,分别为:

返回填充了参数类型#include <fstream> #include <iterator> #include <algorithm> #include <stdexcept> #include <cstdint> template<class Container> Container binary_load(std::string const & bin_file_name) { std::ifstream in(bin_file_name,std::ios::binary); if (!in) { throw std::runtime_error("Could not open \"" + bin_file_name + "\" for reading"); } std::noskipws(in); // PON 1 return Container(std::istream_iterator<std::uint8_t>(in), std::istream_iterator<std::uint8_t>()); //PON 2 } template<class Container> void binary_save(Container && data, std::string const & bin_file_name) { std::ofstream out(bin_file_name,std::ios::binary); if (!out) { throw std::runtime_error("Could not open \"" + bin_file_name + "\" for writing"); } std::copy(data.begin(),data.end(), std::ostream_iterator<std::uint8_t>(out,"")); // PON 3 } 的STL容器 输入文件的字节序列。

将参数类型#include <vector> #include <string> using namespace std; int main(int argc, char *argv[]) { string infile = argv[1]; string outfile = infile + ".saved"; auto data(binary_load<vector<std::uint8_t>>(infile)); binary_save(data,outfile); return 0; } 的STL容器的元素复制到a 输出文件中的字节序列。

std::vector<std::uint8_t>

要编译基本用例,请附加:

.saved

这可以编译为C ++ 11或更高版本。生成的程序将加载您指定的文件作为其第一个 命令行参数进入in然后只是 将该向量保存到具有附加名称的同名文件中 扩展名Container。当然,你的程序将加载一个向量 并保存另一个。

注意事项(PON):

  1. 此声明需要通知流[begin,end) 它应该提取所有字节,而不是跳过空白字节。

  2. 此语句直接从begin构建已填充的std::istream_iterator<char>(in) 迭代器范围,以每个STL容器的构造方式。 in迭代器end流式启动 std::istream_iterator<char>()的迭代器和std::ostream_iterator<char>迭代器out 是每个流的 end-of-stream 迭代器。

  3. 此语句将字节序列复制到a的连续位置 ""最初位于Container的开头。 迭代器的构造函数的binary_load参数通知它 空字符串(即无)将分隔连续的输出字节。

  4. 这些功能模板比您更严格一般 需要:

    • 您调用uint8_t的{​​{1}}类型无需使用 容器uint8_t或甚至是相同大小的容器。它需要 只能是一个容器类型,可以从一个迭代器范围构造 序列Container

    • 同样,您调用binary_save所需的E类型也需要 只有一个元素属于隐式类型uint8_t的元素 可转换为E,但需要注意截断 如果您无意中选择保存uint8_t中无法表示的任何vector<uint8_t>

    因此,将这些放在一起,不会造成任何伤害,例如,如果你 在示例程序中将vector<long>替换为Container

    当然,如果你错误地用一个函数模板调用了 容器类型不满足模板的要求 uint8_t,代码无法编译。

    继续OP的评论

      

    我可以使用unsigned char代替[uint8_t]吗?

    是的,unsigned char几乎不可避免地被uint8_t定义为你的... #include <type_traits> template<class Container, typename Byte = std::uint8_t> Container binary_load(std::string const & bin_file_name) { static_assert(sizeof(Byte) == 1,"Size of `Byte` must be 1"); // `std::uint8_t` becomes `Byte` ... } template<class Container, typename Byte = std::uint8_t> void binary_save(Container && data, std::string const & bin_file_name) { static_assert(sizeof(Byte) == 1,"Size of `Byte` must be 1"); // `std::uint8_t` becomes `Byte` ... } 编译器和任何8位类型的整数类型都可以。 {{1}}只是 最清楚地说“字节”。如果你愿意的话 进一步参数化关于“字节”的模板函数 类型,你可以这样做:

    {{1}}

    关于霍夫曼编码文件的正确文件扩展名,没有 事实上的标准。选择你喜欢的。

    除非您需要使用MS VC10(支持补丁C ++ 11) 对于您的控制台版本,没有必要。 Bang up-to-date GCC toolchains are freely available for Windows,并 支持IDE:CodeLiteCode::Blocks

答案 1 :(得分:1)

我建议使用uint8_t的固定大小的缓冲区:

const unsigned int BUFFER_SIZE = 1024*1024;  
uint8_t buffer[BUFFER_SIZE];
// ...
my_file.read((char *)buffer, BUFFER_SIZE);

在你的程序中,你会读取一个缓冲区,处理它,然后从输入中读取另一个缓冲区。

为了您的目的,数组是比std::vectorstd::list更高效的容器。

另外,请使用uint8_t,因为它是标准化类型。

答案 2 :(得分:1)

您的问题涉及两个截然不同的主题。

  1. 我如何阅读&amp;写一个二进制文件
  2. 如何操作二进制数据。 (充气/放气)
  3. 文件IO

    两种流行的文件阅读方法是read()getline()。我在处理二进制文件时使用read(),在每行读取文本文件时使用getline()。由于您正在处理二进制数据,我建议使用read()

    // Open Binary file at the end
    std::ifstream input(filePath, std::ios::ate | std::ios::binary);
    assert(input.is_open());
    
    // Calculate size
    size_t end = input.tellg();
    input.seekg(0,std::ios::beg);
    size_t beg = input.tellg();
    size_t len = end - beg;
    assert(len > 0);
    
    // Read in Binary data
    std::vector<char> binaryData(len);
    input.read(&(binaryData[0]),len);
    
    // Close
    input.close();
    

    在游戏的这个阶段,您将所有二进制数据存储在一个向量中。我知道在你的例子中你曾使用list来表达,但鉴于你想要处理连续的字节流,vector似乎更符合你的行为。

    <强>二进制

    有几种方法可以处理二进制数据。您可以使用可靠的移位运算符<<>>以及一些良好的&和/或|逻辑。但是,如果您希望在代码中使用更直观的表示,我建议您查看std::bitset

    使用bitset,您可以轻松地将vector的内容加载到二进制的8位表示中。

    std::bitset<8>  deflatedBinary(binaryData[0]);
    std::bitset<12> inflatedBinary;
    

    第一个bitset保存第一个char的8位二进制表示,第二个集合inflatedBinary有十二个全部归零。从这里,您可以通过索引[]来访问其元素。您可以详细了解std::bitset here

相关问题