【问题标题】:Equivalent of a python generator in C++ for buffered reads等效于 C++ 中用于缓冲读取的 python 生成器
【发布时间】:2011-06-08 19:45:04
【问题描述】:

Guido Van Rossum 在article 中演示了 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 位整数的排序列表。我发现了这个不错的 code 用于合并向量。

我被困在如何为一个未知长度的文件制作一个 ifstream 将自己呈现为一个 vector 可以愉快地迭代直到文件末尾到达。有什么建议?我是否使用 istreambuf_iterator 找到了正确的树?

【问题讨论】:

  • 文件中的整数是否用换行符、空格等分隔?如果是这样,istream_iterator 就是你的朋友。
  • @Toolbox 感谢您的回复和解决方案!文件的格式是二进制、64 位 little-endian 无符号整数,没有分隔符。所以看起来 istreambuf_iterator 是要走的路吗?你能帮忙把 istreambuf_iterator 伪装成一个向量吗?
  • 当然!给我一些时间来整理一些东西(并确保它编译)。
  • 为了将来的参考,生成器可以看作是 C++ 中的输入迭代器。显然,语法不是那么好,因为您必须自己维护状态,但它确实有效:)
  • 对不起!我完全忘记发布istreambuf_iterator 的用法示例。我已经在我的答案中编辑了一个,但我并不完全满意。

标签: c++ python algorithm file io


【解决方案1】:

为了将ifstream(或实际上是任何输入流)伪装成类似于迭代器的形式,您需要使用istream_iteratoristreambuf_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 用于一次读取一个字符的文件,而忽略输入的格式。也就是说,它将返回所有字符,包括空格、换行符等。根据您的应用程序,这可能更合适。

注意:Scott Meyers 在Effective STL中解释了为什么上面需要istream_iterator 的单独变量声明。通常,你会这样做:

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

然而,C++ 实际上以一种非常奇怪的方式解析第二行。它将它视为一个名为my_ints 的函数的声明,该函数接受两个参数并返回一个vector&lt;int&gt;。第一个参数的类型为istream_iterator&lt;int&gt;,命名为infile(括号被忽略)。第二个参数是一个没有名称的函数指针,它接受零个参数(因为括号)并返回一个istream_iterator&lt;int&gt;类型的对象。

很酷,但如果你不注意的话也会很烦人。


编辑

这是一个使用 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 位字符,这将让这更容易。

【讨论】:

  • 我目前没有今天的选票,但如果我还有任何剩余,我会投赞成票。 :-) 这是一个很好的解释。
  • @templatetypedef 别担心,我不是:P
  • IIRC 还有一些方法可以添加额外的括号来解决解析歧义,但这很丑陋并且在明确性方面失败了。
  • 因为我喜欢命名事物 --> 您问题的最后一部分是处理所谓的最令人头疼的解析。顺便说一句,答案很好。
猜你喜欢
  • 2019-05-03
  • 2015-02-09
  • 1970-01-01
  • 1970-01-01
  • 2011-05-20
  • 2021-03-18
  • 2016-11-29
相关资源
最近更新 更多