在 istream 上使用regex_iterator

Using a regex_iterator on an istream

本文关键字:regex iterator istream      更新时间:2023-10-16

我希望能够解决这样的问题: 获取 std :: ifstream 来处理 LF、CR 和 CRLF? 其中istream需要由复杂的分隔符标记; 这样标记istream的唯一方法是:

  1. 一次istream一个字符地阅读它
  2. 收集字符
  3. 命中分隔符时,将集合作为标记返回

正则表达式非常擅长使用复杂的分隔符标记字符串:

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

但我不能在istream:(上使用regex_iterator我的解决方案是啜饮istream,然后在上面运行regex_iterator,但啜饮步骤似乎是多余的。

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

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

  1. 由于我们知道regex一次可以工作 1 个字符,因此这个问题要求使用库一次解析 1 个字符istream,而不是在内部一次读取和解析 1 个字符istream
  2. 由于一次解析 1 个字符istream仍将该字符复制到临时变量(缓冲区),因此此代码试图避免在内部缓冲所有代码,而是依赖于库来抽象

C++11 的regex使用 ECMA-262,它不支持向前看或向后看: https://stackoverflow.com/a/14539500/2642059 这意味着regex可以仅使用input_iterator_tag进行匹配,但显然在 C++11 中实现的那些不能。

另一方面,boost::regex_iterator确实支持boost::match_partial标志(在 C++11regex标志中不可用)。boost::match_partial允许用户啜饮文件的一部分并在其上运行regex,在由于输入结束而导致不匹配时,regex将在正则表达式中的该位置"握住手指"并等待更多添加到缓冲区中。您可以在此处看到一个示例:http://www.boost.org/doc/libs/1_55_0/libs/regex/doc/html/boost_regex/partial_matches.html 在一般情况下,如"AnBrCnr",这可以节省缓冲区大小。

boost::match_partial有 4 个缺点:

  1. 在最坏的情况下,就像"ABCn"这样,这不会为用户节省任何尺寸,他必须啜饮整个istream
  2. 如果程序员可以猜出缓冲区大小太大,即它包含分隔符和大量缓冲区大小,则减少缓冲区大小的好处就会被浪费掉。
  3. 每当选择的缓冲区大小太小时,与整个文件的啜饮相比,都需要额外的计算,因此此方法在分隔符密集字符串中表现出色
  4. 包含boost总是会导致膨胀

回过头来回答这个问题:一个标准的库regex_iterator不能在input_iterator_tag上运行,需要整个istreamboost::regex_iterator允许用户可能比整个istream咕噜咕噜。因为这是一个关于代码外观的问题,而且因为boost::regex_iterator最坏的情况无论如何都需要整个文件,所以它不是这个问题的一个好答案。

为了获得最佳代码外观,最好选择对整个文件进行扫描并对其运行标准regex_iterator

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

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