在istream上使用regex_iterator

5
我希望能够解决像这个问题一样的问题:如何让std::ifstream处理LF,CR和CRLF?其中一个istream需要通过复杂的分隔符进行标记化;唯一的标记化istream的方法是:

  1. 逐个字符读取istream
  2. 收集这些字符
  3. 当遇到分隔符时,将收集的内容作为标记返回

使用正则表达式可以很好地将字符串与复杂分隔符分词:

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的奇怪组合?或者如果我需要它,我必须自己编写它?

2个回答

3
这个问题是关于代码外观的:
1. 由于我们知道regex一次只能处理一个字符,所以这个问题要求使用库来逐个字符解析istream,而不是内部读取和解析istream中的一个字符。 2. 由于逐个解析istream仍然会将该字符复制到临时变量(缓冲区),因此该代码试图避免在内部缓冲所有代码,而依赖于库来抽象化。
C++11的regex使用ECMA-262,它不支持向前或向后查找:https://dev59.com/BmUq5IYBdhLWcg3wYvh6#14539500 这意味着一个regex可以仅使用input_iterator_tag进行匹配,但显然C++11中实现的那些不支持。
另一方面,boost::regex_iterator支持boost::match_partial标志(在C++11 regex标志中不可用)。boost::match_partial允许用户吞咽文件的部分并在其上运行regex,如果由于输入结束而导致不匹配,则regex将在该位置“停留”,等待更多内容被添加到缓冲区。您可以在此处查看示例: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",这对用户不会节省任何空间,他必须吞咽整个istream 2. 如果程序员能够猜测一个包含分隔符和相当多的额外内容的缓冲区大小,那么减少缓冲区大小的好处将被浪费 3. 任何时候所选的缓冲区大小过小,与整个文件的吞咽相比,将需要进行额外的计算,因此该方法在密集分隔符字符串中表现出色。 4. 包含boost总是会导致膨胀。
回答这个问题:标准库的regex_iterator不能在input_iterator_tag上操作,需要整个istream读入。而boost::regex_iterator允许用户可能只读入部分istream。但由于这是一个关于代码外观的问题,而且boost::regex_iterator的最坏情况仍然需要读入整个文件,因此它不是对这个问题的好答案。
为了得到最佳的代码外观,读取整个文件并运行标准的regex_iterator是您最好的选择。

"C++11的正则表达式使用ECMA-262,不支持后顾和先行断言。但是C++确实支持先行断言:https://en.cppreference.com/w/cpp/regex/ecmascript。" - hbobenicio

1
我不这么认为。 istream_iterator 具有 input_iterator_tag 标签,而 regex_iterator 期望使用双向迭代器(bidirectional_iterator_tag)进行初始化。
如果您的分隔符正则表达式足够复杂,可以避免自己读取流,那么确实最好将 istream 读入缓冲区。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接