[前言]
笔者常习惯使用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;
}