改进正则表达式以匹配文件中的每一行,包括 windows and/or linux 换行符,即使在 EOF 处缺少换行符

Improving regex to match each line in a file, including windows and/or linux line break, even for missing line break at EOF

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

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

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

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

对此的几点评论:

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

建议的正则表达式可以改进 this:

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

前瞻保证至少有一个字符与 .*(\r?\n)? 一起匹配,这可以防止文件末尾的空字符串匹配。

在性能方面,上面两个正则表达式中的哪一个应该更好?有没有其他更好的方式来匹配我的要求?

如果您使用 ^/$ 锚点或类似锚点,请对此发表评论,因为它们的行为取决于引擎是否默认将它们视为多行。

当每个后续模式无法在字符串中的相同位置匹配时,正则表达式的最佳性能得以实现。 .\R是相反的模式,.用于匹配除换行字符以外的任何字符,\R用于匹配任何换行序列。

在 C++ Boost 正则表达式的上下文中,. 匹配任何字符,包括换行字符,并且 ^$ 锚点是行(不是字符串)“终止符”,您可以考虑使用的模式是

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

参见regex demo详情:

  • (?-s) - 关闭单行模式,. 现在无法匹配换行字符
  • ^ - 行首(boost::regex 语法不需要 (?m) 来生成 ^ line-aware,这是默认行为)
  • (?!\z) - 如果当前位置位于字符串
  • 的末尾,则匹配失败的否定前瞻
  • .* - 除换行字符外的任何零个或多个字符,尽可能多(此模式将正则表达式索引移到行尾)
  • \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
'