Эквивалент генератора Python в C ++ для буферизованного чтения - PullRequest
3 голосов
/ 14 января 2011

Гвидо Ван Россум демонстрирует простоту Python в этой статье и использует эту функцию для буферизованного чтения файла неизвестной длины:

def intsfromfile(f):
    while True:
        a = array.array('i')
        a.fromstring(f.read(4000))
        if not a:
            break
        for x in a:
            yield x

Мне нужно сделатьто же самое в C ++ по соображениям скорости!У меня есть много файлов, содержащих отсортированные списки 64-разрядных целых чисел без знака, которые мне нужно объединить.Я нашел этот замечательный кусок кода для слияния векторов.

Я застрял на том, как сделать ifstream для файла неизвестной длины, представленного в виде vector , который можно успешно перебирать до тех пор, пока не будет достигнут конец файла.Какие-либо предложения?Я лаю правильное дерево с istreambuf_iterator ?

1 Ответ

7 голосов
/ 14 января 2011

Чтобы замаскировать ifstream (или, фактически, любой входной поток) в форме, которая действует как итератор, вы хотите использовать шаблонный класс istream_iterator или istreambuf_iterator. Первый полезен для файлов, где форматирование имеет значение. Например, файл, полный целых чисел, разделенных пробелами, может быть считан в конструктор диапазона итератора вектора следующим образом:

#include <fstream>
#include <vector>
#include <iterator> // needed for istream_iterator

using namespace std;

int main(int argc, char** argv)
{
    ifstream infile("my-file.txt");

    // It isn't customary to declare these as standalone variables,
    // but see below for why it's necessary when working with
    // initializing containers.
    istream_iterator<int> infile_begin(infile);
    istream_iterator<int> infile_end;

    vector<int> my_ints(infile_begin, infile_end);

    // You can also do stuff with the istream_iterator objects directly:
    // Careful! If you run this program as is, this won't work because we
    // used up the input stream already with the vector.

    int total = 0;
    while (infile_begin != infile_end) {
        total += *infile_begin;
        ++infile_begin;
    }

    return 0;
}

istreambuf_iterator используется для чтения файлов по одному символу за раз, независимо от форматирования ввода. То есть он вернет вам все символы, включая пробелы, символы новой строки и так далее. В зависимости от вашего приложения это может быть более подходящим.

Примечание: Скотт Мейерс объясняет в Effective STL , почему отдельные объявления переменных для istream_iterator необходимы выше. Обычно вы делаете что-то вроде этого:

ifstream infile("my-file.txt");
vector<int> my_ints(istream_iterator<int>(infile), istream_iterator<int>());

Однако C ++ фактически анализирует вторую строку невероятно странным образом. Он видит это как объявление функции с именем my_ints, которая принимает два параметра и возвращает vector<int>. Первый параметр имеет тип istream_iterator<int> и называется infile (паратезы игнорируются). Второй параметр - это указатель на функцию без имени, который принимает нулевые аргументы (из-за паразитов) и возвращает объект типа istream_iterator<int>.

Довольно круто, но и довольно обидно, если вы не следите за этим.


EDIT

Вот пример использования istreambuf_iterator для чтения в файле 64-разрядных чисел, расположенных в сквозном порядке:

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

using namespace std;

int main(int argc, char** argv)
{
    ifstream input("my-file.txt");
    istreambuf_iterator<char> input_begin(input);
    istreambuf_iterator<char> input_end;

    // Fill a char vector with input file's contents:
    vector<char> char_input(input_begin, input_end);
    input.close();

    // Convert it to an array of unsigned long with a cast:
    unsigned long* converted = reinterpret_cast<unsigned long*>(&char_input[0]);
    size_t num_long_elements = char_input.size() * sizeof(char) / sizeof(unsigned long);

    // Put that information into a vector:
    vector<unsigned long> long_input(converted, converted + num_long_elements);

    return 0;
}

Мне лично скорее не нравится это решение (использование reinterpret_cast, отображение массива char_input), но я недостаточно знаком с istreambuf_iterator, чтобы удобно использовать один шаблонизированный над 64-битными символами, что сделать это намного проще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...