在 istream 上使用 regex_iterator

Using a regex_iterator on an istream

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

  1. 一次阅读istream一个字符
  2. 收集角色
  3. 当分隔符被击中时 return 集合作为标记

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

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(); });

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

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

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

如果您的定界符正则表达式足够复杂以致于避免自己读取流,那么最好的方法是确实吸食 istream.

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

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

C++11 的 regexes 使用 ECMA-262,它不支持向前看或向后看: 这意味着 regex 可以仅使用 input_iterator_tag,但显然在 C++11 中实现的那些没有。

另一方面,

boost::regex_iterator 确实支持 boost::match_partial 标志(即 not available in C++11 regex flags.) boost::match_partial allows the user to slurp part of the file and run the regex over that, on a mismatch due to end of input the regex will "hold it's finger" at that position in the regex and await more being added to the buffer. You can see an example here: http://www.boost.org/doc/libs/1_55_0/libs/regex/doc/html/boost_regex/partial_matches.html 在一般情况下,如 "A\nB\rC\n\r",这可以节省缓冲区大小。

boost::match_partial 有 4 个缺点:

  1. 在最坏的情况下,像 "ABC\n" 这样可以节省用户 no 的大小,他必须吞下整个 istream
  2. 如果程序员可以猜到缓冲区大小过大,即它包含分隔符和更多的数量,那么减少缓冲区大小的好处就会被浪费掉
  3. 任何时候选择的缓冲区大小太小,与整个文件的 slurping 相比都需要额外的计算,因此这种方法在分隔符密集的字符串中表现出色
  4. 包含 boost 总是会导致膨胀

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

为了最好的代码外观,吞噬整个文件并运行一个标准的regex_iterator是你最好的选择。