在istream上使用regex_iterator

时间:2015-04-27 12:16:45

标签: c++ regex iterator istream istream-iterator

我希望能够解决这样的问题:Getting std :: ifstream to handle LF, CR, and CRLF?其中istream需要由复杂的分隔符进行标记化;这样,istream标记的唯一方法是:

  1. 一次在istream个字符中阅读
  2. 收集字符
  3. 当命中分隔符时,将该集合作为标记返回
  4. 正则表达式非常擅长使用复杂分隔符标记字符串:

    string foo{ "A\nB\rC\n\r" };
    vector<string> bar;
    
    // This puts {"A", "B", "C"} into bar
    transform(sregex_iterator(foo.cbegin(), foo.cend(), regex("(.*)(?:\n\r?|\r)")), sregex_iterator(), back_inserter(bar), [](const smatch& i){ return i[1].str(); });
    

    但是我无法在regex_iterator上使用istream :(我的解决方案是淹没istream,然后在regex_iterator上运行它,但是啜食步骤似乎是多余的。

    在某处有istream_iteratorregex_iterator的邪恶组合,或者如果我想要它,我必须自己写吗?

2 个答案:

答案 0 :(得分:2)

这个问题是关于代码外观的:

  1. 因为我们知道regex一次只能处理1个字符,所以这个问题要求使用库一次解析istream 1个字符,而不是在内部读取和解析{ {1}}一次1个字符
  2. 由于一次解析istream 1个字符仍会将该一个字符复制到临时变量(缓冲区),因此该代码试图避免在内部缓冲所有代码,具体取决于库而不是抽象
  3. C ++ 11&#39; istream使用的ECMA-262不支持预见或后视:https://stackoverflow.com/a/14539500/2642059这意味着regex只能匹配使用regex,但显然那些在C ++ 11中实现的不会。

    另一方面,

    input_iterator_tag支持boost::regex_iterator标记(not available in C++11 regex flags。)boost::match_partial允许用户啜饮部分该文件并运行boost::match_partial,由于输入结束不匹配,regex将&#34;握住它的手指&#34;在正则表达式中的那个位置并等待更多的添加到缓冲区。您可以在此处查看示例:http://www.boost.org/doc/libs/1_55_0/libs/regex/doc/html/boost_regex/partial_matches.html在一般情况下,如regex,这可以节省缓冲区大小。

    "A\nB\rC\n\r"有4个缺点:

    1. 在最糟糕的情况下,如同boost::match_partial这样可以保存用户没有大小,并且他必须啜饮整个"ABC\n"
    2. 如果程序员可以猜到一个太大的缓冲区大小,即包含分隔符和更多的分隔符,那么减少缓冲区大小的好处就会被浪费
    3. 如果选择的缓冲区大小太小,与整个文件的啜食相比,将需要额外的计算,因此这种方法在分隔符密集的字符串中表现优异
    4. 包含istream总是导致膨胀
    5. 回过头来回答这个问题:标准库boost无法在regex_iterator上运行,需要整个input_iterator_tagistream允许用户可能比整个boost::regex_iterator更少。因为这是关于代码外观的问题,并且因为istream的最坏情况需要整个文件的淤塞,所以这不是一个很好的答案。

      为了获得最佳代码外观,整个文件以及运行标准boost::regex_iterator是最好的选择。

答案 1 :(得分:1)

我想不是。 istream_iterator具有input_iterator_tag标记,而regex_iterator期望使用双向迭代器(bidirectional_iterator_tag)进行初始化。

如果您的分隔符正则表达式足够复杂以避免自己阅读流,那么最好的方法就是确实淹没istream