boost翻译(4):boost.iostreams指南--使用Devices

boost.iostreams指南--使用Devices

1.1. 概要: Devices, stream_buffer and stream

使用Boost Iostreams库能很容易创建一个stream类: 你只需简单的写一个类,在类中构造Device概念的模型,并将这个类作为模板参数传给stream或者stream_buffer。

#include  < boost / iostreams / stream.hpp >
#include 
< boost / iostreams / stream_buffer.hpp >

namespace  io  =  boost::iostreams;

class  my_device  /* */ } ;

typedef io::stream
< my_device >          my_stream;
typedef io::stream_buffer
< my_device >   my_streambuf;


在这里io::stream_buffer<my_device>是std::basic_streambuf的继承类,io::stream<my_device>是std::basic_istream的继承类,至于是std::basic_ostream还是std::basic_iostream将取决于my_device的模式,例如对应的i/o读写操作都将依赖于my_device。

#include  < ostream >
#include 
< boost / iostreams / device / file.hpp >
#include 
< boost / iostreams / stream.hpp >

namespace  io  =  boost::iostreams;

int  main()
{
    io::stream_buffer
<file_sink> buf("log.txt");
    std::ostream                 
out(&buf);
    
// out writes to log.txt
}


    在这个例子红, ostream将stream_buffer buf当作它的底层数据sink进行输出, 因此,写入到out中的数据都会被转到log.txt. 使用默认构造函数创建out并通过out.rdbuf(&buf)使用stream buffer buf 也可以获得同样的效果。.

    可以通过其他方式使用Boost Iostreams库定义一个新的stream或者stream buffer类,例如从filtering_stream或者filtering_streambuf派生。

1.2. 使用一个container_source
    假设你想写一个 Device来从STL容器中读取字符。一个只支持读取操作的Device被成为一个Source。一个典型的单字符Source如下所示:
#include  < iosfwd >                            //  streamsize
#include  < boost / iostreams / categories.hpp >    //  source_tag

namespace  io  =  boost::iostreams;

class  my_source  {
public:
    typedef 
char        char_type;
    typedef source_tag  category;

    std::streamsize read(
char* s, std::streamsize n)
    
{
        
// Read up to n characters from the underlying data source
        
// into the buffer s, returning the number of characters
        
// read; return -1 to indicate EOF
    }


    
/* Other members */
}
;


在这里成员类型 char_type 表明 my_source处理字符类型, Source通常也是处理char和wchar_t. 成员类型category表明 device支持的基础I/O操作. category标记source_tag 表明只有读操作被支持.

成员函数read 读取最多n个字符到buffer s中,并返回读取的字符数目,当读取字符数目为0并且到达end-of-stream时将返回-1。在通常情况下,一个Source的成员函数read读取的字符数可能比要求读取的数目少,即使没有到达end-of-stream,这种类型的Source被称为非阻塞Source。 非阻塞Devices与标准streams和stream buffers不能很协调相互配合,但是绝大多数devices是阻塞型的。
你可以将上面的例子改下如下:

#include  < boost / iostreams / concepts.hpp >    //  source

class  my_source :  public  source  {
public:
    std::streamsize read(
char* s, std::streamsize n);

    
/* Other members */
}
;


这里的source是一个方便的基类,它提供了成员类型char_type和category。
现在,你准备去写你的container-source。为了简明一些,我们假设你的容器的迭代器是RandomAccessIterators。

#include  < algorithm >                         //  copy, min
#include  < iosfwd >                            //  streamsize
#include  < boost / iostreams / categories.hpp >    //  source_tag

namespace  boost  namespace iostreams namespace example {

template
<typename Container>
class container_source {
public:
    typedef typename Container::value_type  char_type;
    typedef source_tag                      category;
    container_source(Container
& container)
        : container_(container), pos_(
0)
        
{ }
    std::streamsize read(char_type
* s, std::streamsize n)
    
{
        
using namespace std;
        streamsize amt 
= static_cast<streamsize>(container_.size() - pos_);
        streamsize result 
= (min)(n, amt);
        
if (result != 0{
            std::copy( container_.begin() 
+ pos_,
                       container_.begin() 
+ pos_ + result,
                       s );
            pos_ 
+= result;
            
return result;
        }
 else {
            
return -1// EOF
        }

    }

    Container
& container() return container_; }
private:
    typedef typename Container::size_type   size_type;
    Container
&  container_;
    size_type   pos_;
}
;

}
 }
 }
  //  End namespace boost::iostreams:example


这里有几点需要注意
    * 成员type char_type被定义为容器的value_type;
    *成员类型category 告诉 Iostreams库container_source是一个Source模型;
    *一个container_source可以从一个Container实例构造出来。

read()的实现所基于的思想很简单:首先通过计算Container中剩余字符的数目和要求读取数目的最小值来设置被读取的数目, 接着,如果被读取的数目不为0, 从容器中读取相应数目的字符到缓冲区中 并更新从容器中读取的位置.如果读取数目为0,例如,如果所有容器中所有数据都被读取过 (或者容器为空), 返回-1以指示end-of-stream。

你可以参考下例所示来使用一个container_source:

#include  < cassert >
#include 
< string >
#include 
< boost / iostreams / stream.hpp >
#include 
< libs / iostreams / example / container_device.hpp >   //  container_source

namespace  io  =  boost::iostreams;
namespace  ex  =  boost::iostreams::example;

int  main()
{
    
using namespace std;
    typedef ex::container_source
<string> string_source;

    
string                            input = "Hello World!";
    
string                            output;
    io::stream
<string_source>  in(input);
    getline(
in, output);
    assert(input 
== output);
}


最后,我需要声明的一点是Iostreams库提供了一种从STL容器中读取数据的更方便的方式:一个boost::iterator_range实例可以直接被filtering streams以及stream buffers使用,如下列所示:

#include  < cassert >
#include 
< string >
#include 
< boost / iostreams / filtering_stream.hpp >
#include 
< boost / range / iterator_range.hpp >

namespace  io  =  boost::iostreams;

int  main()
{
    
using namespace std;

    
string                 input = "Hello World!";
    
string                 output;
    io::filtering_istream  
in(boost::make_iterator_range(input));
    getline(
in, output);
    assert(input 
== output);
}



1.3.使用一个container_sink
假设你想写一个 Device来向STL容器中添加字符。一个只支持写入操作的Device被成为一个Sink。一个典型的单字符Sink如下所示:

#include  < iosfwd >                            //  streamsize
#include  < boost / iostreams / categories.hpp >    //  sink_tag

namespace  io  =  boost::iostreams;

class  my_sink  {
public:
    typedef 
char      char_type;
    typedef sink_tag  category;

    std::streamsize write(
const char* s, std::streamsize n)
    
{
        
// Write up to n characters to the underlying
        
// data sink into the buffer s, returning the
        
// number of characters written
    }


    
/* Other members */
}
;

此处的成员类型 char_type 表明 my_sink处理字符类型, Sink通常也是处理char和wchar_t. 成员类型category表明 device支持的基础I/O操作. category标记sink_tag 表明只有写操作被支持.

成员函数write写入最多n个字符到buffer s中,并返回写入的字符数目。在通常情况下,一个Sink的成员函数write写入的字符数可能比要求写入的数目少,这种类型的Sink被称为非阻塞Sink。 非阻塞Devices与标准streams和stream buffers不能很协调相互配合,但是绝大多数devices是阻塞型的。
你可以将上面的例子改下如下:

#include  < boost / iostreams / concepts.hpp >    //  sink

class  my_sink :  public  sink  {
public:
    std::streamsize write(
const char* s, std::streamsize n);

    
/* Other members */
}
;

此处的sink是一个方便的基类,它提供了成员类型char_type和category。
现在,你准备去写你的container-sink。

#include  < algorithm >                         //  copy, min
#include  < iosfwd >                            //  streamsize
#include  < boost / iostreams / categories.hpp >    //  sink_tag

namespace  boost  namespace iostreams namespace example {

template
<typename Container>
class container_sink {
public:
    typedef typename Container::value_type  char_type;
    typedef sink_tag                        category;
    container_sink(Container
& container) : container_(container) { }
    std::streamsize write(
const char_type* s, std::streamsize n)
    
{
        container_.insert(container_.end(), s, s 
+ n);
        
return n;
    }

    Container
& container() return container_; }
private:
    Container
& container_;
}
;

}
 }
 }
  //  End namespace boost::iostreams:example


这里有几点需要注意
    * 成员type char_type被定义为容器的value_type;
    *成员类型category 告诉 Iostreams库container_sink是一个Sink模型;
    *一个container_sink可以从一个Container实例构造出来。
     *  write()简单的将指定缓冲的字符使用insert添加到容器中。

你可以使用一个container_sink如下:

#include  < cassert >
#include 
< string >
#include 
< boost / iostreams / stream.hpp >
#include 
< libs / iostreams / example / container_device.hpp >   //  container_source

namespace  io  =  boost::iostreams;
namespace  ex  =  boost::iostreams::example;

int  main()
{
    
using namespace std;
    typedef ex::container_sink
<string> string_sink;

    
string                          result;
    io::stream
<string_sink>  out(result);
    
out << "Hello World!";
    
out.flush();
    assert(result 
== "Hello World!");
}


注意Iostreams默认支持缓冲,the stream out必须flushed ,然后被写入的字符才会出现在内部的string中。

最后,我需要声明的一点是Iostreams库提供了几种向STL容器中写入数据的更方便的方式:
第一,OutputInterators可以直接被filtering streams以及stream buffers使用,如下列所示:

#include  < cassert >
#include 
< iterator >    //  back_inserter
#include  < string >
#include 
< boost / iostreams / filtering_stream.hpp >

namespace  io  =  boost::iostreams;

int  main()
{
    
using namespace std;

    
string                 result;
    io::filtering_ostream  
out(back_inserter(result));
    
out << "Hello World!";
    
out.flush();
    assert(result 
== "Hello World!");
}


第二,Iostreams库提供一种back_inserter,它由于使用insert而不是push_back,在某种程度上要比std::back_inserter效率更高。

#include  < cassert >
#include 
< string >
#include 
< boost / iostreams / device / back_inserter.hpp >
#include 
< boost / iostreams / filtering_stream.hpp >

namespace  io  =  boost::iostreams;

int  main()
{
    
using namespace std;

    
string                 result;
    io::filtering_ostream  
out(io::back_inserter(result));
    
out << "Hello World!";
    
out.flush();
    assert(result 
== "Hello World!");
}



1.4. 使用 container_device

假设你想写一个 Device来从STL容器中读取字符记忆向容器中写入字符。为了使读和写实用,你还支持对容器的定位。有许多种Devices能同时支持读和写;他们直接也存在区别,有些使用两个独立的序列支持输入和输出,有些使用单独的序列支持输入和输出;有些有两个单独的读写定位符,有些只有一个定位符来。细节部分可以参考Modes
    一个使用单个定位符支持读写单字符序列的单字符Devics被称为一个SeekableDevice。一个典型的SeekableDevice如下所示:
   
#include  < iosfwd >                             //  streamsize, seekdir
#include  < boost / iostreams / categories.hpp >     //  seekable_device_tag
#include  < boost / iostreams / positioning.hpp >    //  stream_offset

namespace  io  =  boost::iostreams;

class  my_device  {
public:
    typedef 
char                 char_type;
    typedef seekable_device_tag  category;

    std::streamsize read(
char* s, std::streamsize n)
    
{
        
// Read up to n characters from the underlying data source
        
// into the buffer s, returning the number of characters
        
// read; return -1 to indicate EOF
    }


    std::streamsize write(
const char* s, std::streamsize n)
    
{
        
// Write up to n characters to the underlying
        
// data sink into the buffer s, returning the
        
// number of characters written
    }


    stream_offset seek(stream_offset off, std::ios_base::seekdir way)
    
{
        
// Seek to position off and return the new stream
        
// position. The argument way indicates how off is
        
// interpretted:
        
//    - std::ios_base::beg indicates an offset from the
        
//      sequence beginning
        
//    - std::ios_base::cur indicates an offset from the
        
//      current character position
        
//    - std::ios_base::end indicates an offset from the
        
//      sequence end
    }


    
/* Other members */
}
;

此处的类型 char_type 说明了my_source处理的字符类型, my_source通常只处理单字符和宽字符,成员类型category说明了device支持的i/o操作。 category标志seekable_tag说明read, write以及单序列版本的seek操作都是被支持的.

类型stream_offset在Iostreams库中用来保存stream offsets.

你可以将上面的例子改写如下:

#include  < boost / iostreams / concepts.hpp >    //  seekable_device

class  my_source :  public  seekable_device  {
public:
    std::streamsize read(
char* s, std::streamsize n);
    std::streamsize write(
const char* s, std::streamsize n);
    stream_offset seek(stream_offset off, std::ios_base::seekdir way);

    
/* Other members */
}
;


此处的seekable_device是一个非常方便的基类,它提供了成员类型char_type、category, 以及上例没有使用的成员函数close和imbue。
现在,你准备去写你的 container_device。同样,我们假设你的容器的迭代器是RandomAccessIterators类型。

#include  < algorithm >                         //  copy, min
#include  < iosfwd >                            //  streamsize
#include  < boost / iostreams / categories.hpp >    //  source_tag

namespace  boost  namespace iostreams namespace example {

template
<typename Container>
class container_device {
public:
    typedef typename Container::value_type  char_type;
    typedef seekable_device_tag             category;
    container_device(Container
& container)
        : container_(container), pos_(
0)
        
{ }

    std::streamsize read(char_type
* s, std::streamsize n)
    
{
        
using namespace std;
        streamsize amt 
= static_cast<streamsize>(container_.size() - pos_);
        streamsize result 
= (min)(n, amt);
        
if (result != 0{
            std::copy( container_.begin() 
+ pos_,
                       container_.begin() 
+ pos_ + result,
                       s );
            pos_ 
+= result;
            
return result;
        }
 else {
            
return -1// EOF
        }

    }

    std::streamsize write(
const char_type* s, std::streamsize n)
    
{
        
using namespace std;
        streamsize result 
= 0;
        
if (pos_ != container_.size()) {
            streamsize amt 
=
                static_cast
<streamsize>(container_.size() - pos_);
            streamsize result 
= (min)(n, amt);
            std::copy(s, s 
+ result, container_.begin() + pos_);
            pos_ 
+= result;
        }

        
if (result < n) {
            container_.insert(container_.end(), s, s 
+ n);
            pos_ 
= container_.size();
        }

        
return n;
    }

    stream_offset seek(stream_offset off, std::ios_base::seekdir way)
    
{
        
using namespace std;

        
// Determine new value of pos_
        stream_offset next;
        
if (way == ios_bas::beg) {
            next 
= off;
        }
 else if (way == ios_bas::cur) {
            next 
= pos_ + off;
        }
 else if (way == ios_bas::end) {
            next 
= container_.size() + off - 1;
        }


        
// Check for errors
        if (next < 0 || next >= container_.size())
            
throw ios_base::failure("bad seek offset");

        pos_ 
= next;
        
return pos_;
    }


    Container
& container() return container_; }
private:
    typedef typename Container::size_type   size_type;
    Container
&  container_;
    size_type   pos_;
}
;

}
 }
 }
  //  End namespace boost::iostreams:example


此处有几点需要注意
    * 成员type char_type被定义为容器的value_type;
    *成员类型category 告诉 Iostreams库container_device是一个SeekableDevice模型;
    *一个container_device可以从一个Container实例构造出来。
    *  read 函数的实现和其在container_source的实现是一样的。

    write 函数的实现和其在container_sink中的实现有一点区别。不光是简单地将字符添加到容器中,你首先要确认当前字符位置是在容器中间的某个位置。如果是的话,那么你写入数据的同时,将会覆盖掉容器中从当前位置开始的已有的字符。如果在写入数据时,你处于容器尾部,那么你将在尾部插入剩下的字符。
   
    seek的实现是比较简单的(省略不说)。

你可以如下列所示使用container_device

#include  < cassert >
#include 
< ios >   //  ios_base::beg
#include  < string >
#include 
< boost / iostreams / stream.hpp >
#include 
< libs / iostreams / example / container_device.hpp >

namespace  io  =  boost::iostreams;
namespace  ex  =  boost::iostreams::example;

int  main()
{
    
using namespace std;
    typedef ex::container_device
<string> string_device;

    
string                            one, two;
    io::stream
<string_device>  io(one);
    io 
<< "Hello World!";
    io.flush();
    io.seekg(
0, BOOST_IOS::beg); // seek to the beginning
    getline(io, two);
    assert(one 
== "Hello World!");
    assert(two 
== "Hello World!");
}
 
<think>嗯,用户想了解如何用Boost.IOStreams替换std::fstream。首先,我需要回忆一下Boost.IOStreams的基本概念和功能。这个库提供了更强大的流处理功能,支持过滤流和设备概念,可能比标准库更灵活。 接下来,我应该先比较两者的区别,让用户明白为什么需要替换。比如,Boost.IOStreams支持压缩、加密过滤器,或者自定义设备,而std::fstream功能较为基础。用户可能有更复杂的需求,比如处理不同的数据格式或需要更高的性能。 然后,需要考虑如何具体替换。比如,原本用std::ifstream的地方,改用boost::iostreams::file_source,再包装成流。可能需要展示代码示例,对比原来的std::fstream使用Boost之后的代码。同时,要提到需要包含的头文件和链接的库,比如-lboost_iostreams。 还要注意依赖管理的问题。用户需要安装Boost库,并且正确配置项目,比如CMake中添加find_package和target_link_libraries。这部分可能对新手来说有难度,要详细说明步骤。 另外,可能存在的陷阱或注意事项,比如异常处理、性能差异,或者接口的不同之处。例如,Boost.IOStreams的一些方法可能和标准库略有不同,需要调整代码习惯。 最后,总结适用场景,帮助用户判断是否真的需要引入Boost。如果只是简单的文件操作,可能不需要替换;但如果有高级需求,比如添加过滤流,那Boost.IOStreams会更合适。 需要确保步骤清晰,代码示例正确,语法正确,并且用中文自然表达,避免术语混淆。可能还需要检查LaTeX格式是否正确,比如行内公式用$...$,独立公式用$$...$$,不过这个问题中可能涉及较少数学公式,但如果有代码中的符号,比如模板参数,可能需要处理。 现在组织这些思路,分步骤解释,提供代码对比,并强调依赖管理和适用场景。确保用户能逐步理解替换的过程和考虑因素。</think>使用Boost.IOStreams库替换标准库中的`std::fstream`需要分步骤理解其核心差异与实现方式。以下是结构化说明: --- ### 一、基础概念对比 1. **std::fstream** - C++标准库组件,提供文件读写的基础功能 - 支持文本/二进制模式,但扩展性有限 - 示例代码: ```cpp std::fstream file("data.txt", std::ios::in | std::ios::out); file << "Hello"; // 写入 ``` 2. **Boost.IOStreams** - 提供更高级的流抽象(设备+过滤器) - 支持链式处理(如压缩、加密、转换) - 需要额外安装Boost库并链接`boost_iostreams` --- ### 二、替换关键步骤 #### 1. 基础文件操作替换 ```cpp // 原std::fstream代码 std::ofstream out("file.txt"); out << "Data"; // 替换为Boost #include <boost/iostreams/device/file.hpp> #include <boost/iostreams/stream.hpp> namespace io = boost::iostreams; io::stream_buffer<io::file_sink> buf("file.txt"); std::ostream out(&buf); out << "Data"; ``` #### 2. 添加过滤器(如Gzip压缩) ```cpp #include <boost/iostreams/filter/gzip.hpp> #include <boost/iostreams/filtering_stream.hpp> io::filtering_ostream out; out.push(io::gzip_compressor()); // 添加压缩过滤器 out.push(io::file_sink("data.gz")); out << "Compressed data"; // 自动压缩后写入 ``` --- ### 三、依赖管理与编译 1. **安装Boost库** - Linux: `sudo apt-get install libboost-iostreams-dev` - Windows: 通过Boost官方安装程序或vcpkg 2. **CMake配置示例** ```cmake find_package(Boost REQUIRED COMPONENTS iostreams) target_link_libraries(your_target PRIVATE Boost::iostreams) ``` 3. **编译命令** ```bash g++ -std=c++11 main.cpp -lboost_iostreams ``` --- ### 四、优势与注意事项 #### ✅ 优势 - **扩展性强**:轻松添加加解密、编码转换等过滤器 - **内存映射支持**:通过`mapped_file`高效处理大文件 - **跨平台一致性**:统一接口处理不同OS的文件系统差异 #### ⚠️ 注意事项 - **性能开销**:多层过滤器可能影响性能 - **异常处理**:需显式调用`exceptions()`方法启用异常 - **学习曲线**:需理解`Source/Sink`设备模型 --- ### 五、适用场景建议 - 需要流数据实时处理(如网络传输中的压缩) - 自定义文件格式处理(如解析非标准二进制协议) - 标准库无法满足性能需求时(如内存映射文件) 实际选择需权衡项目复杂度与维护成本,简单文件操作仍推荐优先使用`std::fstream`。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值