改进正则表达式以匹配文件中的每一行,包括 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)?
对此的几点评论:
- 由于三个备选方案不能在同一个地方匹配,我想备选方案的顺序无关紧要,无论引擎是 DFA 还是 NFA;
++
而不是 +
应该可以节省一些内存,但不会节省一些时间,因为不应发生回溯。
来自 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?
- 一个可选的换行序列。
#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
'
我的要求是匹配文本文件的每一行,包括每行的行终止符,最多不包括最后一行的终止符,以考虑残缺的、非 POSIX-compliant 文件在 Windows 上生成;每个行终止符可以是 \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)?
一起匹配,这可以防止文件末尾的空字符串匹配。
在性能方面,上面两个正则表达式中的哪一个应该更好?有没有其他更好的方式来匹配我的要求?
如果您使用 ^
/$
锚点或类似锚点,请对此发表评论,因为它们的行为取决于引擎是否默认将它们视为多行。
当每个后续模式无法在字符串中的相同位置匹配时,正则表达式的最佳性能得以实现。 .
和\R
是相反的模式,.
用于匹配除换行字符以外的任何字符,\R
用于匹配任何换行序列。
在 C++ Boost 正则表达式的上下文中,.
匹配任何字符,包括换行字符,并且 ^
和 $
锚点是行(不是字符串)“终止符”,您可以考虑使用的模式是
(?-s)^(?!\z).*\R?
参见regex demo。 详情:
(?-s)
- 关闭单行模式,.
现在无法匹配换行字符^
- 行首(boost::regex
语法不需要(?m)
来生成^
line-aware,这是默认行为)(?!\z)
- 如果当前位置位于字符串 的末尾,则匹配失败的否定前瞻
.*
- 除换行字符外的任何零个或多个字符,尽可能多(此模式将正则表达式索引移到行尾)\R?
- 一个可选的换行序列。
#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
'