C++ &引用;复写本;a c++;istream?

C++ &引用;复写本;a c++;istream?,c++,parsing,istream,C++,Parsing,Istream,对于我自己的小解析器框架,我尝试定义(类似)以下函数: template <class T> // with operator>>( std::istream&, T& ) void tryParse( std::istream& is, T& tgt ) { is >> tgt /* , *BUT* store every character that is consumed by this operation

对于我自己的小解析器框架,我尝试定义(类似)以下函数:

template <class T>
// with operator>>( std::istream&, T& )
void tryParse( std::istream& is, T& tgt )
{
    is >> tgt /* , *BUT* store every character that is consumed by this operation
    in some string. If afterwards, is.fail() (which should indicate a parsing
    error for now), put all the characters read back into the 'is' stream so that
    we can try a different parser. */
}
模板
//带运算符>>(std::istream&,T&)
void tryParse(标准::istream&is,T&tgt)
{
is>>tgt/*,*但*存储此操作使用的每个字符
在某些字符串中.If之后,是.fail()(这应该指示解析
错误),将读取的所有字符放回“is”流中,以便
我们可以尝试不同的解析器*/
}
然后我可以这样写:(也许不是最好的例子)

/*语法:MyData=|
双对=
IntTriple=*/
类MyData
{公众:
并集{DoublePair dp;IntTriple it;}数据;
布尔双对数;
};
istream和运营商>>(istream和is、MyData和md)
{
/*如果我在这里只使用“is>>md.data.it”,则
运算符>>(…,IntTriple)可能消耗两个Int,然后命中一个
意外的字符,并失败,使它无法阅读这两个
数字在下面的“else”分支中加倍*/
tryParse(is,md.data.it);
如果(!is.fail())
md.isDoublePair=false;
其他的
{
md.isDoublePair=true;
is.clear();
is>>md.data.dp;
}
回报是;
}

非常感谢您的帮助。

这不是流的目的。您应该将要解析的数据读入缓冲区,然后将该缓冲区(最好作为迭代器范围)交给解析它的函数。这可能看起来像这样:

template <class T, class U>
bool tryParse( U & begin, U & end, T & target ) {
    // return true if parse was successful, false otherwise
}

这将在创建矢量时将整个流读取到矢量中。

将字符放回是一件棘手的事情。有些流支持
unget()
putback(somechar)
,但无法保证可以取消设置多少字符(如果有)


一种更可靠的方法是将字符读入缓冲区并对其进行解析,或者存储在第一次解析尝试中读取的字符,并在第二次解析时使用该缓冲区。

不幸的是,流只有非常少和基本的回放支持

上次我需要它的时候,我编写了自己的reader类,它包装了一个流,但是有一个缓冲区可以把东西放回其中,并且只有当缓冲区为空时才从流中读取。这些方法可以从中获取状态,您可以提交状态或回滚到以前的状态。
state类的析构函数中的默认操作是回滚,这样您就可以在不考虑错误处理的情况下提前解析,因为异常只会将解析器的状态回滚到尝试不同语法规则的位置。(我想这叫做回溯。)这里有一个草图:

class parse_buffer {
    friend class parse_state;
public:
    typedef std::string::size_type index_type;

    parse_buffer(std::istream& str);

    index_type get_current_index() const;
    void set_current_index(index_type) const;

    std::string get_next_string(bool skip_ws = true) const;
    char get_next_char(bool skip_ws = true);
    char peek_next_char(bool skip_ws = true); 

    std::string get_error_string() const; // returns string starting at error idx
    index_type get_error_index() const;
    void set_error_index(index_type);

    bool eof() const;

    // ...
};

class parse_state {
public:
    parse_state(parse_buffer&);
    ~parse_state();

    void commit();
    void rollback();

    // ...
};
这应该给你一个想法。它没有任何实现,但这很简单,应该很容易重做。另外,真正的代码有许多方便的函数,比如读取带分隔符的字符串的函数,如果字符串是多个给定关键字中的一个,则使用该字符串,读取字符串并将其转换为每个模板参数给定的类型,以及类似的内容

其思想是,函数将错误索引设置为其起始位置,保存解析状态,并尝试解析,直到成功或陷入死胡同。在后一种情况下,它只会抛出一个异常。这将破坏堆栈上的
parse_state
对象,将状态回滚到一个函数,该函数可以捕获异常并尝试其他操作,或者输出一个错误(这就是
get_error_string()


如果您想要一个真正快速的解析器,这种策略可能是错误的,但是流也经常会变慢。OTOH,上次我使用类似的东西时,我制作了一个XPath解析器,它在专有DOM上运行,用于表示3D渲染器中的场景。并不是XPath解析器从那些试图获得更高帧速率的家伙那里得到了所有的关注<代码>:)

您可以使用流成员做一些有趣的事情。特别是,您可以直接访问缓冲区的指针


但是,您不能保证缓冲区的大小。

这听起来很有趣。你还有代码吗?它是开源的吗?我可以看一下吗?哦,太好了!那么,parse_state::rollback()只是在其parse_缓冲区上调用set_current_index(创建我的索引时的索引)?commit()什么都不做,把parse_缓冲区的索引留在原来的位置?啊,但是commit()可以告诉parse_buffer我们不会在当前位置之前回滚(),因此(至少对于这个parse_状态)可以安全地忘记该位置之前的所有内容。。。是的,我想我开始明白了。我要试试这个,非常感谢@是的,你有这个主意。提交所做的另一件事是将错误索引设置为当前位置,以便以后分析错误时生成当前索引,作为错误文本的起始位置。我模模糊糊地记得,这有它的角落和缝隙(难道不是
parse_state
也必须存储旧的错误索引吗?),但几年前我就使用了这个方案,因此,如果没有旧的代码,我还需要再做一次,我也不会有比上面更多的开始<代码>:)流不是用于此目的的合适工具,因为它们缺乏适当的放回。当设计这样的简单内联解析器时(否则,尝试一下
boost::spirit
),解析函数实际上应该使用一对迭代器。回滚变得很容易(只需在回溯解析器之前保存迭代器值)。
 std::vector< char > buffer(std::istream_iterator<char>(is), std::istream_iterator<char>());
class parse_buffer {
    friend class parse_state;
public:
    typedef std::string::size_type index_type;

    parse_buffer(std::istream& str);

    index_type get_current_index() const;
    void set_current_index(index_type) const;

    std::string get_next_string(bool skip_ws = true) const;
    char get_next_char(bool skip_ws = true);
    char peek_next_char(bool skip_ws = true); 

    std::string get_error_string() const; // returns string starting at error idx
    index_type get_error_index() const;
    void set_error_index(index_type);

    bool eof() const;

    // ...
};

class parse_state {
public:
    parse_state(parse_buffer&);
    ~parse_state();

    void commit();
    void rollback();

    // ...
};