提问人:SupAl 提问时间:9/6/2023 最后编辑:SupAl 更新时间:9/7/2023 访问量:51
使用 istreambuf_iterator 与 copy_n 读取文件
Reading files with istreambuf_iterator vs copy_n
问:
我想解析不同长度的文件中不同类型的块,所以我创建了一个函数,通过传入 ifstream 来读出块,如下所示:
void parse_next(std::ifstream& input_file, std::vector<uint8_t>& data, size_t count)
{
std::copy_n(
std::istreambuf_iterator<char>(input_file),
count,
std::back_inserter(data)
);
}
我希望文件位置增加计数,即
// some init code
size_t const pos_before{input_file.tellg()};
parse_next(input_file, data, count);
size_t const pos_after{input_file.tellg()};
// this assumption is _not_ correct!
assert(count == (pos_after - pos_before));
// but this is!
assert((count - 1) == (pos_after - pos_before));
但是,使用 with count 而不是 count 会给出正确的计数。input_file.read()
std::copy_n
那么这是怎么回事呢?我在istreambuf_iterator的文档中看不到提到这一点的任何地方。
还是惹我了?std::copy_n
请注意,在上面的示例中,我们可以假设还有很多数据需要读取,所以这不是因为文件是空的。 此外,该文件以二进制形式打开。
答:
2赞
John Zwinck
9/7/2023
#1
您正在使用 .它是一个仅输入迭代器。想象一下,你有一个 5 个字节的文件,你读到:istreambuf_iterator
count=2
- 它调用
sgetc
来读取第一个字节。这不会推进流位置。 - 由于 ,需要再一个字节。因此,它增加了流位置。
count=2
copy_n
- 它使用 读取第二个字节。
sgetc
- 由于 ,因此不再需要字节。 返回。
count=2
copy_n
请注意,只有步骤 2 会递增流位置,并且只需要在读取两个字符时调用一次。
是的,这很奇怪。但大多数人只会使用 .我几乎从未见过人们在生产代码中使用......尤其是因为它对于您的用例类型来说效率低下。input_file.read()
istreambuf_iterator
我们可以说,嘿,让我们在返回之前更改以递增迭代器。这将解决这个 0.1% 的用例,但代价是减慢其他用例的速度。copy_n
评论
0赞
SupAl
9/7/2023
所以这就是关键 - 在引擎盖下使用。我真的以为这会超载到或'''memmove'''。但是,当然,对于back_inserter,我们可能需要在此过程中进行一些动态分配。因此,获得最佳性能的标准方法是执行 resize(),然后读取 std::vector(假设我仍然想使用 std::vector,如果我想保留动态分配,这可能是我的最佳选择),或者你有更好的方法吗?sgetc
std::copy_n
memcpy
0赞
Retired Ninja
9/7/2023
这样解释是完全有道理的,这对我来说是违反直觉的,因为我希望实际读取会推进文件指针而不是迭代器。当我看到人们使用这种模式时,通常是读取整个文件,所以在这种情况下,无论哪种方式似乎都无关紧要。谢谢你教我一些新东西。:)
评论
)
data.size()
data.size()
递增 (with and )。修复了缺少的断言 ')'。count
std::copy_n()
.read()
parse_next
需要通过引用接受 vector 参数。尝试此操作时引起了一些小混淆。godbolt.org/z/jzb3co6Wx有趣的是,这个职位比你预期的要少一个,而且读得越多,就会产生重复。我不知道你问题的答案,但它确实让我很高兴我避免写这样的代码。read