Во-первых, конечно, вам нужно указать вещи немного строже, чем у вас есть. Что делать с чем-то вроде "[ 11 12 13; 21 22; 31 32
33 ]"
, например: вставить 0.0
вместо отсутствующего значения или установить 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
.
Кроме того: мы несколько ограничены в приведенном выше ограниченными возможностями для отчетов об ошибках в iostreams. В частности, нам бы очень хотелось различать ввод типа "[11 12 13; 21"
и ничего (настоящее условие конца файла). Но наши попытки прочитать разделитель после "21"
будут устанавливать eofbit
, и мы ничего не можем с этим поделать. (На самом деле мы могли создать новое слово состояния, используя std::ios_base::xalloc()
, устанавливая его тогда и только тогда, когда чтение '['
в начале завершается ошибкой с установленным eofbit
. Но для этого потребуется очень нестандартный способ проверка на наличие ошибок в клиентском коде, что, в свою очередь, создало бы бесконечный поток проблем с обслуживанием.)
Наконец, два мета-комментария: если это кажется сложным... так оно и есть. Ввод почти всегда сложен из-за множества различных условий ошибки, которые вы должны проверить. И, во-вторых, обратите внимание на использование функций для упрощения каждой отдельной операции. Частой ошибкой новичков является то, что они не разбивают такие вещи на части. Это почти всегда плохое программирование, например, наличие вложенного цикла в функции, за исключением случаев применения математических алгоритмов к таким вещам, как Matrix
. В этом случае синтаксический анализ не является математическим алгоритмом, и вы хотите отделить обработку каждой строки от общей обработки; в этом случае также полезно отделить обработку первой строки от остальных, поскольку случаи ошибок различны. (Первая строка может иметь любую длину больше 0, последующие строки должны иметь ту же длину, что и предыдущие строки.)
person
James Kanze
schedule
22.12.2011