提问人:snake_style 提问时间:8/14/2019 最后编辑:TriskalJMsnake_style 更新时间:8/22/2019 访问量:1687
C++ getline() 和 boost::iostreams::filtering_istream 工作起来很奇怪
C++ getline() with boost::iostreams::filtering_istream works strange
问:
请帮助我了解以下程序行为差异的原因。
该程序从一个源和一个滤波器创建一个测试文本文件和一连串升压滤波器 ()。然后它尝试读取一些行。filtering_istream
#include <iostream>
#include <fstream>
#include <string>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/filtering_stream.hpp>
class my_filter : public boost::iostreams::input_filter
{
public:
explicit my_filter(std::ostream& s) : m_out(s)
{}
template<typename Source>
int get(Source& src)
{
int c = boost::iostreams::get(src);
if(c == EOF || c == boost::iostreams::WOULD_BLOCK)
return c;
if(c == '\r')
return boost::iostreams::WOULD_BLOCK;
if(c == '\n')
{
m_out << m_str << std::endl;
m_str = "";
}
else
{
m_str += c;
}
return c;
}
private:
std::ostream& m_out;
std::string m_str;
};
int main()
{
using namespace std;
boost::iostreams::filtering_istream file;
const std::string fname = "test.txt";
std::ofstream f(fname, ios::out);
f << "Hello\r\n";
f << "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\r\n";
f << "World!\r\n";
f.close();
file.push(my_filter(std::cout));
file.push(boost::iostreams::file_descriptor(fname));
std::string s;
while(std::getline(file, s))
{}
return 0;
}
使用 clang 在线编译显示预期结果:
但是,如果我将字符串“111...111”(128 个)更改为 127 个字符串(255 个以此类推),结果会有所不同:
这种行为在我看来是不正确的。
注意:“111...111”(127 个)的长度与方法中的默认buffer_size相关......boost::iostreams::filtering_istream::push
file.push(my_filter(std::cout), default_buf_size=...)
您可以在此处查看并运行代码:code_example
更新 1
我觉得很奇怪,在某些情况下,返回值允许您进一步读取,而在其他情况下,它认为文件已完成。但根据文档:WOULD_BLOCK
WOULD_BLOCK - 表示输入暂时不可用
因此,它并不表示流的结束。
答:
你想用这部分做什么?
if(c == '\r')
return boost::iostreams::WOULD_BLOCK;
如果你试图忽略字符,那么你应该跳过它们并从源中读取另一个字符。Boost 文档有一个示例可以准确地说明这一点:\r
#include <ctype.h> // isalpha
#include <cstdio.h> // EOF
#include <boost/iostreams/categories.hpp> // input_filter_tag
#include <boost/iostreams/operations.hpp> // get, WOULD_BLOCK
using namespace std;
using namespace boost::iostreams;
struct alphabetic_input_filter {
typedef char char_type;
typedef input_filter_tag category;
template<typename Source>
int get(Source& src)
{
int c;
while ( (c = boost::iostreams::get(src)) != EOF &&
c != WOULD_BLOCK &&
!isalpha((unsigned char) c) )
;
return c;
}
};
这将从源中删除所有非字母字符(请参阅 https://www.boost.org/doc/libs/1_68_0/libs/iostreams/doc/concepts/input_filter.html)。
现在,至于为什么您会看到上述行为,这基本上是正在发生的事情:
您正好从缓冲区边界返回,在当前缓冲区中设置任何字符之前
WOULD_BLOCK
get()
这是从如下所示的实现中调用的(请参阅末尾带有注释的两行):
read()
template<>
struct read_filter_impl<any_tag> {
template<typename T, typename Source>
static std::streamsize read
(T& t, Source& src, typename char_type_of<T>::type* s, std::streamsize n)
{ // gcc 2.95 needs namespace qualification for char_traits.
typedef typename char_type_of<T>::type char_type;
typedef iostreams::char_traits<char_type> traits_type;
for (std::streamsize off = 0; off < n; ++off) {
typename traits_type::int_type c = t.get(src);
if (traits_type::is_eof(c))
return check_eof(off);
if (traits_type::would_block(c)) // It gets HERE
return off; // and returns 0
s[off] = traits_type::to_char_type(c);
}
return n;
}
(https://www.boost.org/doc/libs/1_70_0/boost/iostreams/read.hpp)
- 这反过来又是从这样的代码中调用的:
// Read from source.
std::streamsize chars =
obj().read(buf.data() + pback_size_, buf.size() - pback_size_, next_);
if (chars == -1) {
this->set_true_eof(true);
chars = 0;
}
setg(eback(), gptr(), buf.data() + pback_size_ + chars);
return chars != 0 ?
traits_type::to_int_type(*gptr()) :
traits_type::eof();
(https://www.boost.org/doc/libs/1_70_0/boost/iostreams/detail/streambuf/indirect_streambuf.hpp)
因此,由于它没有读取当前缓冲区中的任何字符,因此它会将其解释为文件结束并完全放弃。
更新
(太长了,无法发表评论)
您不是该函数的用户,该函数实际上是您的代码的用户。您的过滤器介于源和读取器之间。 确实表明输入暂时不可用,但最终由读者决定是否以及何时再次尝试。在这方面尽了最大的努力,它处理它设法从源头获得的任何内容,然后尝试再次读取(这就是为什么它不会在你第一次返回时停止,之后)。但是,如果源没有返回更多内容并且缓冲区是空的,它基本上别无选择,只能认为它到达了源的末尾。它在缓冲区中没有更多的字符要处理,并且无法从源中获取更多字符。WOULD_BLOCK
streambuf
WOULD_BLOCK
Hello
streambuf
如果你把两个连续的放在任何地方,你会看到相同的行为。例如,请尝试以下操作:\r
f << "Hello\r\r\n";
f << "nothing\r\n";
f << "World!\r\n";
请注意后面的两个 s。这与缓冲区大小无关。它只是从源读取,不会返回任何内容。\r
Hello
评论
WOULD_BLOCK
停止从源代码读取,基本上返回它在当前迭代中已经读取的任何内容。因为这恰好发生在缓冲区边界上,所以它还没有读取任何内容,所以它返回 .不返回任何数据的读取被解释为文件结束。设置 eof 标志后,将来从流中读取将失败,因此它将完全停止。0
评论
ios::binary
std::ofstream
std::ofstream