使用-Mre=debug 模块并深入了解细节,我找到了我认为的答案。我删除了前导空格,因为它与问题无关。我删除了除相关部分之外的所有内容。两个正则表达式首先使用 RHS (5:BRANCH) 匹配第二个换行符前面的空格/换行符,然后将指针设置在第二个换行符前面:
案例一:字符串a \n \n b\n
Matching REx "^\s+|\s+$" against "%n b%n"
4 <a %n > <%n b%n> | 0| 1:BRANCH(5)
4 <a %n > <%n b%n> | 1| 2:MBOL(3)
| 1| failed...
4 <a %n > <%n b%n> | 0| 5:BRANCH(9)
4 <a %n > <%n b%n> | 1| 6:PLUS(8)
| 1| POSIXD[\s] can match 2 times out of 2147483647...
6 <a %n %n > <b%n> | 2| 8:MEOL(9)
| 2| failed...
5 <a %n %n> < b%n> | 2| 8:MEOL(9)
| 2| failed...
| 1| failed...
| 0| BRANCH failed...
5 <a %n %n> < b%n> | 0| 1:BRANCH(5) <-- HERE!
5 <a %n %n> < b%n> | 1| 2:MBOL(3)
5 <a %n %n> < b%n> | 1| 3:PLUS(9)
| 1| POSIXD[\s] can match 1 times out of 2147483647...
6 <a %n %n > <b%n> | 2| 9:END(0)
Match successful!
在这种情况下,LHS (1:BRANCH) 首先失败,RHS (5:BRANCH) 失败,所以它向前移动 1 步,直到 LHS 匹配的换行符之后,并删除前面的内容它:一个空格。
在换行符和b 前面的空格匹配时,正则表达式中的“指针”向前移动到换行符前面。
%n> < b%n>
^ \s
案例2:字符串a \n\n b\n
Matching REx "^\s+|\s+$" against "%n b%n"
3 <a %n> <%n b%n> | 0| 1:BRANCH(5) <-- HERE!
3 <a %n> <%n b%n> | 1| 2:MBOL(3)
3 <a %n> <%n b%n> | 1| 3:PLUS(9)
| 1| POSIXD[\s] can match 2 times out of 2147483647...
5 <a %n%n > <b%n> | 2| 9:END(0)
Match successful!
在此字符串中,LHS (1:BRANCH) 中的零宽度断言^ 可以看到字符串左侧的换行符,并允许其匹配。在另一个字符串中,它有一个空格,因此无法匹配。所以 LHS 交流发电机匹配(称为 1:BRANCH),并删除它前面的内容,即换行符和空格 \n 。
不像案例1那样跳过第一次尝试并向前移动1步,它可以直接匹配左侧的换行符,右侧的空格\n :
%n> <%n b%n>
^ \s\s
TL;DR:在您的第二个字符串中,换行符可以匹配两个换行符之间的行首,因此将它们都删除。在第一个字符串中,它不能像那样匹配,因为那里有一个空格,而是向前移动一步,跳过换行符并使用该换行符来匹配字符串的开头。效果是换行符保留在字符串中。
如何避免这种行为?好吧,问题是你的正则表达式太松了。 \n 可以匹配正则表达式 ^、$ 和 \s 的所有组件,以各种组合方式进行。它也可以匹配在字符串的中间。如果您想安全并获得可预测的结果,请在逐行模式下使用正则表达式,不要将文件转换为单个字符串。那么你就不需要多行匹配了,所有的问题都迎刃而解了。
否则,请避免使用多行修饰符,只需照常删除前导和尾随空格,然后在字符串内部修剪多个带有空格的换行符,例如s/\n\s*\n/\n/g。
本质上,您试图同时做太多事情。让你的正则表达式更严格,并尝试一次做一件事情。