我需要以二进制模式读取文件并将字节作为十六进制值存储在任何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文件具有哪些文件扩展名?非常感谢你。
答案 0 :(得分:5)
正如注释所阐明的那样,您希望将二进制文件的字节加载到
一些char
的STL容器 - 或者更准确地说,uint8_t
- 和
将这样的容器保存回二进制文件。
有很多方法可以做到这一点,包括你发现的,使用方法
的std::basic_istream::read
和std::basic_ostream::write
,
或std::istream_iterator
和std::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):
此声明需要通知流[begin,end)
它应该提取所有字节,而不是跳过空白字节。
此语句直接从begin
构建已填充的std::istream_iterator<char>(in)
迭代器范围,以每个STL容器的构造方式。
in
迭代器end
是流式启动
std::istream_iterator<char>()
的迭代器和std::ostream_iterator<char>
迭代器out
是每个流的 end-of-stream 迭代器。
此语句将字节序列复制到a的连续位置
""
最初位于Container
的开头。
迭代器的构造函数的binary_load
参数通知它
空字符串(即无)将分隔连续的输出字节。
这些功能模板比您更严格一般 需要:
您调用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:CodeLite,Code::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::vector
或std::list
更高效的容器。
另外,请使用uint8_t
,因为它是标准化类型。
答案 2 :(得分:1)
您的问题涉及两个截然不同的主题。
文件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。