在将数据持久化到文件时,你可能会发现很难强制要求系统将特定的部分数据写到一行中。将特定的数据写到同一行有时是很有" />
龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 软件开发 > JAVA开发 >

用line_as_stream 简化流的读取

时间:2009-12-23 15:42来源:未知 作者:admin 点击:
分享到:
?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 在将数据持久化到文件时,你可能会发现很难强制要求系统将特定的部分数据写到一行中。将特定的数据写到同一行有时是很有

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 


  在将数据持久化到文件时,你可能会发现很难强制要求系统将特定的部分数据写到一行中。将特定的数据写到同一行有时是很有用的,比如在你从流(如一个文件)中读取一个数组的时候。
  
  假设你要读取一个数组的元素,其中有一行被破坏了(比如丢失了一些数据)。一般情况下,这会导致后面所有的元素都受损。
  
  作为一个例子,假设我们有一个数据结构,是一个窗口数组,你希望把它持久化到一个文件中,象下面这样:
  
  第一行:窗口的数量
  后面的每一行都包含两个值:窗口的宽度和窗口的高度
  写成代码似乎很简单:
  
  #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, " "));
  /*在这里加一个调试断点:看看你读了多少个错误的值! */
  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 = ' ')
  {
  std::basic_string< char_type, char_traits> strLine;
  std::getline( streamIn, strLine, chDelim);
  return strLine;
  }


  
精彩图集

赞助商链接