Эффективное получение подпотока потока строк - PullRequest
0 голосов
/ 16 августа 2011

Мне нужно реализовать механизм, где я могу инициализировать вектор моего пользовательского класса, используя текстовый источник, где каждая строка источника представляет один экземпляр моего класса.Чтобы добиться этого, я реализовал operator >> для своего класса и stringstream.Когда я читаю источник, я иду построчно и получаю подпоток из моего исходного источника, а затем каждый раз анализирую подпоток.Это имеет три преимущества для меня.Во-первых, таким образом я могу убедиться, что одна строка исходного текста будет представлять ровно один экземпляр моего класса.Во-вторых, так как остальная часть строки после синтаксического анализа игнорируется, я могу безопасно добавить любой комментарий в любую строку моего текстового источника, который наверняка будет проигнорирован синтаксическим анализатором.И в-третьих, мне не нужно упоминать длину вектора в моем исходном источнике, так как в первый раз я получаю ошибку синтаксического анализа (я проверяю биты fail и bad потока, чтобы подтвердить это), я знаючто объявление вектора закончено.

Для разбора строки за строкой я использую следующий код:

std::stringstream       fullStream;
std::stringstream       lineStream;
std::string             str;
bool                    isValid;
myClass                 newInstance;
std::vector < myClass > result;

// Fill fullStream from external source (codepart omitted)
isValid = true;
while ( isValid && ! fullStream.eof ( ) ) {
   std::getline ( fullStream, str );
   lineStream.clear ( );
   lineStream.str ( str );
   lineStream >> newInstance;
   isValid = ! lineStream.fail ( );
   if ( isValid ) {
      result.push_back ( newInstance );
   }
}

Хотя этот код работает нормально, мне интересно, есть либыл лучший способ достичь того же результата.Особенно, если бы был более эффективный способ извлечь строку из fullStream до lineStream.

Спасибо,Ádám

Ответы [ 2 ]

3 голосов
/ 16 августа 2011

Во-первых, если код работает, это действительно только случайно.Идиоматический способ обработки этого:

std::string line;
while ( std::getline( fullStream, line ) ) {
    std::istringstream lineStream( line );
    lineStream >> newInstance;
    if ( lineStream ) {
        result.push_back( newInstance );
    } else {
        fullStream.setstate( std::ios_base::failbit );
    }
}

Проверка eof() перед чтением редко бывает полезна, и не проверка результатов вашего getline перед его использованием почти наверняка является ошибкой.Попытка повторно использовать stringstream более сложна и подвержена ошибкам, чем просто создание нового;есть все виды состояний, которые могут или не могут быть сброшены.У потоков есть механизм запоминания состояния ошибки, поэтому вы, вероятно, захотите использовать это.(Если вы хотите продолжать использовать fullStream для других вещей после ошибки, проблема более сложная, потому что вы уже извлекли строку, которая не удалась, и вы не можете ее вернуть.) И если вы толькочитая, вы должны использовать std::istringstream, а не std::stringstream (в котором много лишнего багажа);в общем, очень и очень редко можно использовать двунаправленный поток.

2 голосов
/ 16 августа 2011

Одной из очевидных альтернатив будет ваше operator>> построчное чтение, так что вам не нужно делать это внешне:

class MyClass { 
    // some sort of data to demonstrate the idea:
    int x;
    std::string y;

    friend std::istream &operator>>(std::istream &is, MyClass &m) { 
        std::string temp;
        std::getline(is, temp);
        std::istringstream buffer(temp);
        buffer >> m.x >> m.y;
        return is;
    }
};

При этом код для чтения данныхиз файла становится немного более простым:

std::copy(std::istream_iterator<MyClass>(fullStream),
          std::istream_iterator<MyClass>(),
          std::back_inserter(result));

Редактировать: если вы не хотите встроить линейно-ориентированное чтение непосредственно в operator>> для MyClass, другой возможностью является использованиепрокси-класс:

class LineReader { 
    MyClass object;
public:
    operator MyClass() { return object; }

    friend std::istream &operator>>(std::istream &is, LineReader &d) { 
        std::string line;
        std::getline(is, line);
        std::istringstream buffer(line);
        buffer >> d; // delegate to the object's own stream-oriented reader.
    }
};

Затем, когда вы хотите выполнить линейно-ориентированное чтение, вы читаете объекты прокси-класса, но храните объекты исходного класса:

std::vector<MyClass>((std::istream_iterator<LineReader>(some_stream)), 
                      std::istream_iterator<LineReader>());

Но, когда / если вы хотите прочитать поток объектов вместо строк объектов, вы напрямую используете собственный operator>> объекта:

std::vector<MyClass>((std::istream_iterator<MyClass>(stream),
                      std::istream_iterator<MyClass>());
...