用C++中STL提供的fstream和stream_iterator读写二进制文件

(转载)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;

}

 

 

 

你可能感兴趣的:(iterator)