改进正则表达式以匹配文件中的每一行,包括Windows和/或linux换行符,即使在EOF时缺少换行符

问题描述

我的要求是匹配一个文本文件的每一行,包括每一行的行终止符,最多不包括最后一行的终止符,以考虑到Windows上生成的残缺,非POSIX兼容文件;每个行终止符可以是\n\r\n

我正在寻找性能最佳的正则表达式。

我能想到的第一个正则表达式是this

\n|\r\n|[^\r\n]++(\r\n|\n)?

一些评论

  • 由于三个备选方案无法在同一位置匹配,因此我认为这些备选方案的顺序无关紧要,而与引擎是DFA还是NFA无关;
  • ++而不是+应该节省一些内存,但不会节省一些时间,因为不应该发生回溯。

Code Review中,建议使用.*(\r?\n|$)(如果[^\r\n]*(\r?\n|$)也与.\n相匹配,则使用\r),但是这有一个缺陷:它也匹配文件末尾的空字符串。

建议的正则表达式可以像this一样进行改进:

(?=.).*(\r?\n)?

其中的前瞻性保证了.*(\r?\n)?至少匹配一个字符,从而防止了文件末尾的空字符串匹配。

性能而言,以上两个正则表达式中哪个应该更好?根据我的要求,还有其他更好的匹配方法吗?

请使用^ / $锚或类似的锚,请对此发表评论,因为它们的行为取决于引擎认情况下是否将其视为多行。

解决方法

当每个后续模式在字符串中的相同位置均不匹配时,可以在regex中获得最佳性能。 .\R是相反的模式,.用于匹配除换行符以外的任何字符,\R用于匹配任何换行符。

在C ++ Boost regex的上下文中,.与任何字符匹配,包括换行符,而^$的锚点是行(不是字符串)“终止符”,即您所使用的模式可以考虑使用is

(?-s)^(?!\z).*\R?

请参见regex demo详细信息

  • (?-s)-关闭单行模式,.现在将无法匹配换行符
  • ^-行的开头(boost::regex语法不需要(?m)来使^感知行,这是默认行为)
  • (?!\z)-如果当前位置位于字符串的最末端,则否定超前行为将使匹配失败
  • .*-除换行符以外的任何零个或多个字符,并且尽可能多(此模式将regex索引移到行尾)
  • \R?-可选的换行符序列。

这里是C++ boost::regex demo

#include <boost/regex.hpp>
#include <iostream>
#include <string>

int main()
{
  std::string text = "Line1\nLine2\r\nLine3\rLastLine\n";
  boost::regex expression(R"((?-s)^(?!\z).*\R?)");
  boost::smatch match;
  boost::sregex_token_iterator iter(text.begin(),text.end(),expression,0);
  boost::sregex_token_iterator end;
  for( ; iter != end; ++iter ) {
   std::cout << "'" << *iter << "'" << std::endl;
  }
  return 0;
}

输出:

'Line1
'
'Line2
'
'Line3
'
'LastLine
'