Кое-что о чтении частей данных из файла с использованием C ++ istream_iterator - PullRequest
2 голосов
/ 18 февраля 2012

Цель: Существует текстовый файл (на жестком диске), содержащий целые числа, разделенные каким-либо разделителем.

Пример:

5245
234224
6534
1234

Мне нужно прочитать их в контейнер STL.

int main(int argc, char * argv[]) {
  using namespace std;

  // 1. prepare the file stream
  string fileName;
  if (argc > 1)
    fileName = argv[1];
  else {
    cout << "Provide the filename to read from: ";
    cin >> fileName;
  }
  unique_ptr<ifstream, ifstream_deleter<ifstream>> ptrToStream(new ifstream(fileName, ios::out));
  if (!ptrToStream->good()) {
    cerr << "Error opening file " << fileName << endl;
    return -1;
  }

  // 2. value by value reading will be too slow on large data so buffer data
  typedef unsigned int values_type;
  const int BUFFER_SIZE(4); // 4 is for testing purposes. 16MB or larger in real life
  vector<values_type> numbersBuffer(BUFFER_SIZE);
  numbersBuffer.insert(numbersBuffer.begin(), istream_iterator<values_type>(*ptrToStream), istream_iterator<values_type>());
  // ...

Основным недостатком этого кода является как мне справиться с проблемой, когда размер файла очень велик, поэтому я не могу сохранить все его содержимое в памяти ? Я также не хочу использовать push_back, поскольку он неэффективен по сравнению с интервалом insert.


Итак, вопрос в следующем: как я могу прочитать не более BUFFER_SIZE элементов из файла, эффективно используя STL?

Ответы [ 2 ]

4 голосов
/ 18 февраля 2012

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

std::vector<T> buffer;
buffer.reserve(size);
std::istreambuf_iterator<T> it(in), end;
for (std::vector<T>::size_type count(0), capacity(size);
     it != end && count != capacity; ++it, ++count) {
    buffer.push_back(*it);
}

Я понимаю, что вы не хотите push_back(), потому что это якобы медленно. Однако, по сравнению с операцией ввода-вывода, я сомневаюсь, что вы сможете измерить небольшие издержки, особенно с типичной реализацией библиотеки ввода-вывода.

Просто для округления на примере обернутого итератора: ниже приведен пример того, как может выглядеть оболочка подсчета для std::istream_iterator<T>. Есть много разных способов сделать это, это только один из них.

#include <iostream>
#include <iterator>
#include <vector>
#include <sstream>

template <typename T>
class counted_istream_iterator:
    public std::iterator<std::input_iterator_tag, T, std::ptrdiff_t>
{
public:
    explicit counted_istream_iterator(std::istream& in): count_(), it_(in) {}
    explicit counted_istream_iterator(size_t count): count_(count), it_() {}

    T const& operator*() { return *this->it_; }
    T const* operator->() { return it_->it_.operator->(); }
    counted_istream_iterator& operator++() {
        ++this->count_; ++this->it_; return *this;
    }
    counted_istream_iterator operator++(int) {
        counted_istream_iterator rc(*this); ++*this; return rc;
    }

    bool operator== (counted_istream_iterator const& other) const {
        return this->count_ == other.count_ || this->it_ == other.it_;
    }
    bool operator!= (counted_istream_iterator const& other) const {
        return !(*this == other);
    }
private:
    std::ptrdiff_t           count_;
    std::istream_iterator<T> it_;
};

void read(int count)
{
    std::istringstream in("0 1 2 3 4 5 6 7 8 9");
    std::vector<int>   vec;
    vec.insert(vec.end(), counted_istream_iterator<int>(in),
               counted_istream_iterator<int>(count));
    std::cout << "size=" << vec.size() << "\n";
}

int main()
{
    read(4);
    read(100);
}
0 голосов
/ 18 февраля 2012

Возможен способ решения моей проблемы:

// 2. value by value reading will be too slow on large data so buffer data
typedef unsigned int values_type;
const int BUFFER_SIZE(4);
vector<values_type> numbersBuffer;
numbersBuffer.reserve(BUFFER_SIZE);
istream_iterator<values_type> begin(*ptrToStream), end;
while (begin != end) {
  copy_n(begin, BUFFER_SIZE, numbersBuffer.begin());
  for_each(numbersBuffer.begin(), numbersBuffer.end(), [](values_type const &val){ std::cout << val << std::endl; });
  ++begin;
}

Но у него есть один недостаток. Если входной файл содержит следующее:

8785
245245454545
7767

, тогда 8785 будет прочитано, а 245245454545 и 7767 - нет, потому что 245245454545 нельзя преобразовать в unsigned int Ошибка будет молчать. (

...