Использование итератора C ++ для чтения списка из файла? - PullRequest
0 голосов
/ 19 апреля 2011

Я пытаюсь прочитать / десериализовать список элементов из файла (а затем отфильтровать некоторые из них).Полезно ли использовать для этой цели итератор?

Моя текущая попытка

#include <boost/iterator/iterator_adaptor.hpp>
class ReadIterator : public boost::iterator_adaptor<ReadIterator, Elem *, boost::single_pass_traversal_tag>
{
public:
    explicit ReadIterator(const char *filename) : reader(filename)  {}

private:
    friend class boost::iterator_core_access;

    void increment() {
         this->base_reference() = reader.readNext();
    }

    Reader reader;
};

Это не освобождает память должным образом (например, readNew возвращает указатель на новый Elem)Какой правильный способ сделать это?Кроме того, как на самом деле использовать такой итератор, как определить конец?Или есть ли лучший подход, чем использование итератора?

Ответы [ 3 ]

4 голосов
/ 19 апреля 2011

Простой способ сделать это - использовать std :: istream_iterator

std::vector<YourObjectClass>    data;

std::remove_copy_if(std::istream_iterator<YourObjectClass>(file),
                    std::istream_iterator<YourObjectClass>(),
                    std::back_inserter(data),
                    YourFilter
                   );

Стандартный алгоритм копирует объекты (типа YourObjectClass) из входных данных file и помещает их в вектор data если функтор фильтра возвращает true.

Единственными условиями являются:

  • В YourObjectClass должен быть оператор потока ввода
  • YourFilter должен перегрузить оператор () для объектовYourObjectClass или быть функцией, которая принимает параметр типа YourObjectClass.

Простой рабочий пример:

  • Мой объект представляет собой строку.
  • ОтфильтроватьСтроки, начинающиеся с буквы «A»

Пример:

#include <vector>
#include <string>
#include <fstream>
#include <iterator>
#include <algorithm>

struct Line
{
    std::string   data;
};
std::istream& operator>>(std::istream& stream, Line& line)
{
    return std::getline(stream, line.data);
}
struct AFilter
{
    bool operator()(Line const& line) const
    {
        return line.data.size() > 0 && line.data[0] == 'A';
    }
};

int main()
{
    std::ifstream       file("Plop");
    std::vector<Line>   data;

    std::remove_copy_if(std::istream_iterator<Line>(file),
                        std::istream_iterator<Line>(),
                        std::back_inserter(data),
                        AFilter()
                       );
}
1 голос
/ 19 апреля 2011

Хорошо использовать итератор для этой цели. Вы не указали, что существующий istream_iterator не будет работать для вашей цели. По крайней мере, в большинстве случаев вы можете просто написать operator>> для одного элемента и использовать std::istream_iterator для чтения списка этих элементов из файла.

1 голос
/ 19 апреля 2011

Вместо readNext(), возвращая необработанный указатель на элемент, можете ли вы сконструировать вызов так, чтобы он возвращал умный указатель со счетчиком ссылок, который автоматически освободит свои ресурсы, когда счетчик ссылок на указатель станет равным нулю? Либо так, либо вам нужно будет найти способ получить указатель обратно, чтобы вы могли вызвать delete на нем, прежде чем выделять больше памяти при следующем вызове readNext(), когда increment() вызывается снова.

Что касается "конца", то, что вы могли бы сделать в этом случае, - это провести некоторый тест в вашем классе Reader, который определяет, достиг ли вы конца файла или какого-либо другого сценария окончания. Если да, верните false, в противном случае верните true. Например:

bool increment()
{
    if (reader.not_end_of_file())
    {
        this->base_reference() = reader.readNext();
        return true;
    }

    return false;
}

Так что теперь вы можете вызывать increment() в цикле некоторого типа, и вы будете знать, когда вы достигнете конца файла или другого конца, потому что функция вернет false.

...