我想用以下正则表达式从多行字符串中删除前导空格和尾随空格:
s/^s*|s*$//mg
在这个例子中,它似乎或多或少地工作得很好:
perl -e '$_=" a n n bn"; s/^s*|s*$//mg; print "$_n";'
这给出了结果:
a
b
(我没想到的是,中间有空格的双色已经变成了单色)
但请注意:
perl -e '$_=" a nn bn"; s/^s*|s*$//mg; print "$_n";'
结果:
ab
现在这两个都消失了,多行字符串现在是单行,这不是我想要的。如果这不是bug,我该如何避免这种行为?
使用-Mre=debug
模块并深入了解细节,我找到了我认为的答案。我去掉了前导空格,因为它与问题无关。除了相关部件,我什么都拆了。两个正则表达式首先使用RHS(5:BRANCH)匹配第二个换行符前面的空格/换行符,然后在第二个新行前面设置指针:
案例1:字符串a n n bn
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。
在换行符和b
前面的空格之间的匹配中;指针";在正则表达式中,已向前移动到换行符的前面。
%n> < b%n>
^ s
情况2:字符串a nn bn
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
。
它可以直接在左边的换行符和右边的空白n
上进行匹配,而不是像案例1那样跳过第一次尝试并向前移动1步:
%n> <%n b%n>
^ ss
TL;DR:在第二个字符串中,换行符可以匹配两个换行符之间的行首,因此可以同时删除它们。在第一个字符串中,它不能像那样匹配,因为那里有一个空格,而是向前移动一步,跳过换行符并使用该换行符来匹配字符串的开头。其效果是换行符保留在字符串中。
你怎样才能避免这种行为?问题是正则表达式太松散了。n
可以以各种组合匹配正则表达式^
、$
和s
的所有组件。它也可以在字符串的中间进行匹配。如果您希望安全并获得可预测的结果,请以逐行模式使用regex,不要将文件拖入单个字符串。这样你就不需要多行匹配了,所有的问题都会迎刃而解。
否则,请避免使用多行修饰符,只需像往常一样删除前导和尾部空白,然后在字符串内部修剪多个带空格的换行符,类似于s/ns*n/n/g
。
从本质上讲,你试图同时做太多的事情。使正则表达式更加严格,并尝试一次只做一件事。
s
可以匹配换行符,这导致了删除换行符的问题。
将s
替换为以下内容之一:
h
仅删除水平空白字符。虽然它不匹配换行符,但也不匹配其他垂直空白字符[1](?[ s - n ])
这需要use experimental qw( regex_sets );
在5.36之前。但添加此功能并使用该功能是安全的,早在5.18中将其作为实验功能引入时,因为从那时起该功能就没有任何更改[^Sn]
匹配一个既不是非空白字符也不是换行符的字符,也就是说一个非换行符的空白字符
下面详细介绍了您的模式是如何匹配的。
对于
␠ a ␠ ␊ ␠ ␊ ␠ b ␊
0 1 2 3 4 5 6 7 8 9
图案
/^s*|s*$/m
生成以下匹配项:
- Pos 0,len 1:
␠
与^s*
匹配 - Pos 2,len 3:
␠␊␠
与s*$
匹配。XXX - Pos 5,len 0:
s*$
匹配的空字符串 - Pos 6,len 1:
␠
与^s*
匹配 - Pos 8,len 1:
␊
与s*$
匹配。XXX - Pos 9,len 0:
^s*
匹配的空字符串
对于
␠ a ␠ ␊ ␊ ␠ b ␊
0 1 2 3 4 5 6 7 8
图案
/^s*|s*$/m
生成以下匹配项:
- Pos 0,len 1:
␠
与^s*
匹配 - Pos 2,len 2:
␠␊
与s*$
匹配。XXX - Pos 4,len 2:
␊␠
与^s*
匹配。XXX - Pos 7,len 1:
␊
与s*$
匹配。XXX - Pos 8,len 0:
^s*
匹配的空字符串
脚注:
垂直空白:
- U+000A管线进料
- U+000B行制表
- U+000C表单馈送
- U+000D回车
- U+0085下一行
- U+2028线路分离器
- U+2029段落分隔符