用line_as_stream 简化流的读取

王朝other·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

在将数据持久化到文件时,你可能会发现很难强制要求系统将特定的部分数据写到一行中。将特定的数据写到同一行有时是很有用的,比如在你从流(如一个文件)中读取一个数组的时候。

假设你要读取一个数组的元素,其中有一行被破坏了(比如丢失了一些数据)。一般情况下,这会导致后面所有的元素都受损。

作为一个例子,假设我们有一个数据结构,是一个窗口数组,你希望把它持久化到一个文件中,象下面这样:

第一行:窗口的数量

后面的每一行都包含两个值:窗口的宽度和窗口的高度

写成代码似乎很简单:

#include

#include

#include

struct Window

{

Window( int nLength = 0, int nHeight = 0)

: m_nWindowLength( nLength), m_nWindowHeight( nHeight)

{}

int m_nWindowLength;

int m_nWindowHeight;

};

std::ostream & operator << ( std::ostream & streamOut, const Window & value)

{

streamOut << value.m_nWindowLength << " " << value.m_nWindowHeight;

return streamOut;

}

std::istream & operator >> ( std::istream & streamIn, Window & value)

{

streamIn >> value.m_nWindowLength >> value.m_nWindowHeight;

return streamIn;

}

void write_windows( std::vector< Window> &aWindows, const char * strFileName)

{

std::ofstream streamOut( strFileName);

// 第一行

streamOut << aWindows.size() << std::endl;

// 其余行

std::vector< Window>::iterator itFirst = aWindows.begin(), itLast = aWindows.end();

while ( itFirst != itLast)

{

// 每个窗口的数据都在它自己那一行

streamOut << *itFirst << std::endl;

++itFirst;

}

}

但是,要正确地读出这些数据,可能会有一些问题:

//可能出错!!!

void read_windows( std::vector< Window> &aWindows, const char * strFileName)

{

aWindows.clear();

std::ifstream streamIn( strFileName);

int nSize;

streamIn >> nSize;

for ( int idx = 0; idx < nSize; ++idx)

{

Window w;

streamIn >> w;

aWindows.push_back( w);

}

}

上面的代码并没有强制任何东西。所有数据都被放到一行中,这看起来没有什么问题。但假如用户不小心,修改了你的文件,插入了一个多余的值或删掉了一个值,那么后面所有的元素都会得到错误的值,而你的程序并不会意识到这一点。尝试运行一下下面的代码并仔细看看其中的注释:

#include

#include

int main(int argc, char* argv[])

{

std::vector< Window> aWindows;

aWindows.push_back( Window( 100, 400));

aWindows.push_back( Window( 200, 400));

aWindows.push_back( Window( 400, 400));

aWindows.push_back( Window( 500, 500));

aWindows.push_back( Window( 600, 200));

aWindows.push_back( Window( 600, 400));

aWindows.push_back( Window( 600, 690));

write_windows( aWindows, "persist.txt");

std::vector< Window> aReadWindows;

/* 在这里加一个调试断点;

修改persist.txt,删除第4行的第一个值*/

read_windows( aReadWindows, "persist.txt");

std::copy( aReadWindows.begin(), aReadWindows.end(),

std::ostream_iterator< Window>( std::cout, "\n"));

/*在这里加一个调试断点:看看你读了多少个错误的值! */

return 0;

}

还好,你可以用来line_as_stream读取一行,然后将它看作一个流。用这种方法,你可以确定每个元素是从一行中读取的。于是,read_windows函数变成这样:

void read_windows( std::vector< Window> &aWindows, const char * strFileName)

{

aWindows.clear();

std::ifstream streamIn( strFileName);

int nSize;

// 第一行

line_as_stream( streamIn) >> nSize;

for ( int idx = 0; idx < nSize; ++idx)

{

Window w;

//每个窗口的数据都在它自己那一行

line_as_stream( streamIn) >> w;

aWindows.push_back( w);

}

}

现在,重新运行前面的例子,你可以看到只有一个元素受损,如你所料。

这就是line_as_stream的源码:

#include

#include

#include

namespace Private

{

template< class char_type, class char_traits>

struct line_stream_holder

{

typedef line_stream_holder< char_type, char_traits> this_class;

typedef std::basic_istringstream< char_type, char_traits> stream_type;

typedef std::basic_string< char_type, char_traits> string_type;

line_stream_holder( const string_type & value)

: m_stream( value)

{}

line_stream_holder( const this_class & source)

: m_stream( source.m_stream.str() )

{}

// allow passing this stream in functions that

// accept streams

operator stream_type & () const

{ return m_stream; }

private:

mutable stream_type m_stream;

};

template< class char_type, class char_traits, class value_type>

inline typename line_stream_holder< char_type, char_traits>::stream_type & operator >> (const line_stream_holder< char_type, char_traits> & streamIn, value_type & value)

{

typedef typename line_stream_holder< char_type, char_traits>::stream_type stream_type;

stream_type & underlyingStream = streamIn;

underlyingStream >> value;

return underlyingStream;

}

} // namespace Private

template< class char_type, class char_traits>

Private::line_stream_holder< char_type, char_traits> line_as_stream(

std::basic_istream< char_type, char_traits> & streamIn, char_type chDelim = '\n')

{

std::basic_string< char_type, char_traits> strLine;

std::getline( streamIn, strLine, chDelim);

return strLine;

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
 
© 2005- 王朝網路 版權所有 導航