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!");
}
 
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下 4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值