讨论

错误示范:

  • 错误示范 1:\n —— 不匹配 Windows 行尾(CRLF);
  • 错误示范 2:\r\n|\n —— 不匹配旧式 mac 行尾(CR);
  • 错误示范 3:[\r\n]{1,2} —— 错误匹配“LFCR”;
  • 错误示范 4:\r?\n —— 不匹配旧式 mac 行尾(CR);
  • 错误示范 5:\n|\r|\r\n —— CR 和 LF 被拆开,如果存在 CRLF 将匹配出两个子串;
  • 错误示范 6:\r\n?|\n —— 这个的 bug 很难发现,问题在于:如果文件中存在 LFCRLF,则 LF 和 CRLF 都被匹配,但这种情况下应该只有 CRLF 才对;
  • 错误示范 7:\r\n|\n|\r —— 问题同上。

正确示范:\r?\n|(?<!\n)\r —— 使用零宽断言。

这个正则 永远不会匹配到 LFCR;但也有弱点,即 LFCRLF 会匹配两个子串:LF 和 CRLF;但对于长度不大于 2 的纯 LF、CR 序列,都能够正确匹配。

所以就有了更正确一些的示范:\r\n|(?<!\r\n)\n(?!\r\n)|(?<!\r\n|\n)\r(?!\r\n|\n) —— 看起来很长但思路很简单:

  1. 优先匹配 CRLF;
  2. 否则,匹配前后都没有 CRLF 的 LF;
  3. 否则,匹配前后都没有 CRLF 或 LF 的 CR。
  4. 否则,拒绝。

(你也可以调整单独 CR 和单独 LF 的优先级,但 CRLF 的优先级总是最高的)

当存在长度小于等于 3 的纯 LF、CR 构成的序列时,这个正则都能给出正确的匹配,但 LFCRCR 除外,它会匹配两个子串,即 LF 和末尾的 CR;为了解决这个问题就必须进一步增加规则。

我们发现,这样下去是没完没了的……正则表达式的长度取决于你要实现正确匹配的纯 LF、CR 序列的最大长度。

结论

对于不那么极端的情况,\r?\n|(?<!\n)\r 就可以正确匹配了。

抬杠

任何有限长的正则表达式 R R R 都无法正确匹配任意无限长的序列 S S S 中可接受的子串 σ R ( S ) \sigma_R(S) σR(S) Σ \Sigma Σ Σ \Sigma Σ 是所有可接受的子串集合,且 c a r d ( Σ ) > 1 card(\Sigma) > 1 card(Σ)>1),使匹配到的子串集合 c a r d ( Σ ′ ) = 1 card(\Sigma ') = 1 card(Σ)=1.

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐