正如 cmets 所阐明的,您希望将二进制文件的字节加载到
char 的一些 STL 容器 - 或更准确地说,uint8_t - 和
将这样的容器保存回二进制文件。
有很多方法可以做到这一点,包括如您所发现的,使用
std::basic_istream::read
和std::basic_ostream::write,
或std::istream_iterator
和std::ostream_iterator。
后一种方法产生最简单的代码。 fread/fwrite 方法
产生最快的代码,但更简单的是显然是更好的
将只是您程序的序幕和尾声操作。
这是一对匹配的模板函数,它们将分别:
返回参数类型 Container 的 STL 容器,填充为
输入文件的字节序列。
将参数类型Container的STL容器的元素复制到一个
输出文件中的字节序列。
#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
}
要编译一个基本用例,请附加以下内容:
#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;
}
这编译为 C++11 或更好。生成的程序加载您指定为其第一个文件
命令行参数到std::vector<std::uint8_t> 然后只是
将该向量保存到同名的文件中
扩展.saved。当然,您的程序将加载一个向量
并保存一个不同的。
注意事项 (PON):
需要此语句来通知流in
它应该提取所有个字节,而不是跳过空白字节。
此语句直接从[begin,end) 构造填充的Container
迭代器范围,以构造每个 STL 容器的方式。
begin 迭代器 std::istream_iterator<char>(in) 是 start-of-stream
in 的迭代器和 end 迭代器 std::istream_iterator<char>()
是每个流的 end-of-stream 迭代器。
此语句将字节序列复制到一个连续位置
std::ostream_iterator<char> 最初位于 out 的开头。
迭代器构造函数的 "" 参数通知它
空字符串(即无)应分隔连续的输出字节。
这些函数模板比你严格的更通用
要求:
调用 binary_load 时使用的 Container 类型不必是
uint8_t 的容器,甚至是相同大小的容器。它需要
只能是可以从迭代器范围构造的容器类型
uint8_t 的序列。
同样,您调用 binary_save 的 Container 类型需要
仅是其元素的类型为 E 的类型,即隐式
可转换为uint8_t,但需要注意的是会发生截断
如果您随意选择保存在uint8_t 中无法表示的任何Es。
因此,将这些放在一起不会造成任何伤害,例如,如果您
在示例程序中将vector<uint8_t> 替换为vector<long>。
当然,如果您错误地使用
不满足模板要求的容器类型
Container,代码无法编译。
OP 的 cmets 继续
我可以使用 unsigned char 代替 [uint8_t] 吗?
是的,uint8_t 几乎不可避免地被您定义为 unsigned char
编译器,任何 8 位类型的整数类型都可以。 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`
...
}
关于霍夫曼编码文件的正确文件扩展名,没有
事实标准。选择你喜欢的。
除非你需要使用 MS VC10(有不完整的 C++11 支持)
对于您的控制台版本,无需。 Bang up-to-date GCC toolchains
are freely available for Windows,和
支持IDE:CodeLite,Code::Blocks