首先,当然,你需要指定比你更严格的东西。您应该如何处理类似"[ 11 12 13; 21 22; 31 32
33 ]"
的内容,例如:0.0
为缺失值插入 a 或 set
failbit
?
除此之外,使用std::vector
来收集输入将使事情变得更容易一些。类似于以下内容,例如:
template< typename T >
char getRow( std::istream& source, std::vector<T>& dest )
{
dest.clear();
char separator;
source >> separator;
while ( source && separator != ';' && separator != ']' ) {
source.unget();
T tmp;
source >> tmp;
if ( source ) {
dest.push_back( tmp );
source >> separator;
}
}
if ( source && dest.empty() ) {
dest.setstate( std::ios_base::failbit );
}
return source ? separator : '\0';
}
template< typename T >
char getFirstRow( std::istream& source,
std::vector<std::vector<T> >& dest )
{
dest.clear();
std::vector<T> row;
char separator = getRow( source, row );
if ( source ) {
if ( row.empty() ) {
dest.setstate( std::ios_base::failbit );
} else {
dest.push_back( row );
}
}
return source ? separator : '\0';
}
template< typename T >
char getFollowingRow( std::istream& source,
std::vector<std::vector<T> >& dest )
{
std::vector<T> row;
char separator = getRow( source, row );
if ( source ) {
if ( row.size() != dest.front().size() ) {
dest.setstate( std::ios_base::failbit ) ;
} else {
dest.push_back( row );
}
}
return source ? separator : '\0';
}
template< typename T >
std::istream&
operator>>( std::istream& source, Matrix<T>& dest )
{
char separator;
source >> separator;
if ( separator != '[' ) {
source.setstate( std::ios_base::failbit );
} else {
std::vector<std::vector<T> > results;
separator = getFirstRow( source, results );
while ( separator == ';' ) {
separator = getFollowingRow( source, results );
}
if ( separator != ']' ) {
source.setstate( std::ios_base::failbit );
}
if ( source ) {
dest.assign( results );
}
}
return source;
}
当然,这意味着该Matrix<T>::assign
函数必须能够设置尺寸。并且为了可用,Matrix<T>
需要一个默认构造函数,它可能“推迟”实际构造直到
Matrix<T>::assign
.
另外:由于 iostream 中错误报告的可能性有限,我们在上述方面受到了一些限制。特别是,我们真的很想区分 input like"[11 12 13; 21"
和 nothing(真正的文件结束条件)。但是我们尝试读取分隔符之后"21"
会设置eofbit
,我们对此无能为力。(实际上,我们可以创建一个新的状态字,使用std::ios_base::xalloc()
, 设置它当且仅当,'['
在开始的读取失败并
eofbit
设置。但这需要一种非常不标准的方法来检查客户端代码中的错误,这将在反过来会造成层出不穷的维护问题。)
最后,两个元评论:如果这看起来很复杂......它是。输入几乎总是复杂的,因为您必须检查所有各种错误条件。其次,注意使用函数来保持每个单独的操作(有点)简单。初学者经常犯的错误是不要像这样分解事物——几乎总是糟糕的编程,例如,在函数中有一个嵌套循环,除非将数学算法应用于诸如
Matrix
. 在这种情况下,解析不是数学算法,你希望将每一行的处理与整体处理分开;在这种情况下,将第一行的处理与其他行分开处理也很有用,因为错误情况不同。(第一行的长度可以大于 0,后面的行必须与前面的行长度相同。)