(转载)http://blog.chinaunix.net/uid-22342666-id-1774790.html
[前言]
笔者常习惯使用C库中的FILE*来操作文件,但是C++的STL中是提供了fstream等文件流对象,于是乎便刻意的改变自己的一些习惯,让程序看起来更C++一些。
这是笔者在最近写的一个程序中的片段,由于常常把写的一些小模块给弄丢了,故在此留个记号,若对你有所帮助,欢迎常来看看~
:) wangxinus, 2009.9
[正文]
需要解决的问题是这样的:在一个2进制的数据文件中,整齐的排列着以6bytes对齐的数据,其实是一组指令,每一个指令都是这样的结构[命令码 操作码1 操作码2],宽度为2+2+2bytes。把这里指令读出来,然后再做其他操作。
这个问题很简单,的确。建立一个如下的结构体:
struct Opcode
{
uint16_t cmd;
uint16_t op1;
uint16_t op2;
};
然后打开文件,每次读取sizeof(struct Opcode)长度,并填入结构中,然后放入一个链表 std::list<Opcode> OpList。
以前,我肯定会这么做。但是为什么我不能做得更C++一些呢?C++提供了输入输出流,流指示器(iterator:主流翻译是 迭代器)std::istream_iterator, 还有泛型算法 copy。STL提供了强大的泛型运算,那么我们就应该好好利用,写出来可以是这样的:
std::ifstream inFile(fileName.c_str(), std::ios::in|std::ios::binary);
std::istream_iterator<Opcode> is(inFile);
std::istream_iterator<Opcode> eof;
std::list<Opcode> thisList;
/* 前面都是一些定义,真正完成数据读入的就是这么一句 */
std::copy(is, eof, back_inserter(thisList));
上面的代码还有一个问题没有解决,这个问题隐藏在copy中,因为我们的文件流还不能够识别Opcode对象,需要我们重载std::istream& operator>>(std::istream& is, Opcode& opcode);
std::istream& operator>>(std::istream& is, Opcode& opcode)
{
is.read(reinterpret_cast<char*>(&opcode), sizeof(Opcode));
return is;
}
这样我们就把数据全部读入内存中,然后我们可以对数据做其他处理了。同理,我们也可以把数据写入文件中。
[注意]
使用fstream时, 一定要把打开文件的方式写清楚,这里是以2进制的方式打开,就需要加上std::ios::binary 标志位。如果不加,在linux上面运行没有问题,但是windows上面就出现了数据读不完的错误, 原因是在*nix系统中,并不区分文本文件和数据文件,windows却区分了,默认的是文本方式。
[代码]
附上一段测试的代码。
/********************************************************** ** Copyleft (C) 2009 wangxinus ** http://wangxinus.cublog.cn ** 用C++中STL提供的fstream和stream_iterator读写二进制文件。 **********************************************************/ #include <iostream> #include <fstream> #include <list> #include <string> #include <iterator> // 测试用的文件 const std::string fileIn = "test.jpg"; const std::string fileOut = std::string("new") + fileIn; class Opcode { public: //...这里定义其他操作 private: uint16_t _cmd; uint16_t _op1; uint16_t _op2; }; inline std::istream& operator>>(std::istream& is, Opcode& opcode) { is.read(reinterpret_cast<char*>(&opcode), sizeof(Opcode)); return is; } inline std::ostream& operator<<(std::ostream& os, const Opcode& opcode) { os.write(reinterpret_cast<const char*>(&opcode), sizeof(Opcode)); return os; } int main() { std::ifstream in(fileIn.c_str(), std::ios::binary | std::ios::in); if(!in) { std::cerr << "Open In file failed!" << std::endl; return -1; } std::list<Opcode> opcodeList; //从文件中读入数据 std::istream_iterator<Opcode> is(in); std::istream_iterator<Opcode> eof; std::copy(is, eof, back_inserter(opcodeList)); //...这里对数据进行一些操作 << std::ofstream out(fileOut.c_str(), std::ios::binary | std::ios::out); if(!out) { std::cerr << "Open Out file failed!" <<std::endl; return -1; } //把数据写入另外一个文件中 std::ostream_iterator<Opcode> os(out, ""); std::copy(opcodeList.begin(), opcodeList.end(), os); std::cout << "Write OK!" << std::endl; return 0; }