Regex 正则表达式之谜
我正在调查一个关于regexp的秘密。我累了,所以我可能会失踪 很明显,但我看不出有什么原因 在下面的示例中,我使用perl,但我首先在VIM中看到了这一点, 所以我猜它与不止一个regexp引擎有关 假设我们有这个文件:Regex 正则表达式之谜,regex,perl,vim,Regex,Perl,Vim,我正在调查一个关于regexp的秘密。我累了,所以我可能会失踪 很明显,但我看不出有什么原因 在下面的示例中,我使用perl,但我首先在VIM中看到了这一点, 所以我猜它与不止一个regexp引擎有关 假设我们有这个文件: $ cat data 1 =2 3 =4 5 =6 7 =8 然后我们可以删除“=”前面的空白,并使用 $ cat data | perl -ne 's,(.)\s+=(.),\1=\2,g; print;' 1=2 3=4 5=6 7=8 注意,在每一行中,
$ cat data
1 =2 3 =4
5 =6 7 =8
然后我们可以删除“=”前面的空白,并使用
$ cat data | perl -ne 's,(.)\s+=(.),\1=\2,g; print;'
1=2 3=4
5=6 7=8
注意,在每一行中,匹配的所有实例都被替换;
我们使用了/g搜索修饰符,它不会在第一次替换时停止,
而是继续替换,直到生产线结束
例如,'=2'前面的空格和
删除了“=4”;在同一条线上
为什么不使用更简单的结构,比如's,=,=,g'?嗯,我们是
为更困难的场景做准备。。。右手边在哪里
其中一个赋值是带引号的字符串,可以是
单引号或双引号:
$ cat data2
1 ="2" 3 ='4 ='
5 ='6' 7 ="8"
做同样的工作(删除等号前的空格),
我们必须小心,因为字符串可能包含相等的
签名-所以我们标记我们看到的第一个报价,并寻找它
通过反向引用:
$ cat data2 | perl -ne 's,(.)\s+=(.)([^\2]*)\2,\1=\2\3\2,g; print;'
1="2" 3='4 ='
5='6' 7="8"
$ cat data3 | perl -ne "s,(\w+)(\s*) =(['\"])([^\3]*)\3,\1\2=\3\4\3,g; print;"
posAndWidth="40:5 =" height ="1"
posAndWidth="-1:8 ='" textAlignment ="Right"
我们使用back引用\2搜索任何不存在的内容
与我们第一次看到的报价相同,任何次数([^\2]*)。
然后,我们搜索原始报价本身(\2)。如果被发现,
我们使用反向引用来引用替换中的匹配零件
目标
现在看看这个:
$ cat data3
posAndWidth ="40:5 =" height ="1"
posAndWidth ="-1:8 ='" textAlignment ="Right"
这里我们要做的是删除存在的最后一个空格字符
在每行中的所有“=”实例之前。像以前一样,我们不能使用
一个简单的's,=“,=”,g',因为字符串本身可能包含
等号
因此,我们遵循与上面相同的模式,并使用反向引用:
$ cat data2 | perl -ne 's,(.)\s+=(.)([^\2]*)\2,\1=\2\3\2,g; print;'
1="2" 3='4 ='
5='6' 7="8"
$ cat data3 | perl -ne "s,(\w+)(\s*) =(['\"])([^\3]*)\3,\1\2=\3\4\3,g; print;"
posAndWidth="40:5 =" height ="1"
posAndWidth="-1:8 ='" textAlignment ="Right"
它是有效的。。。但只在第一场比赛中!
“textAlignment”后面的空格未删除,该空格也未删除
最重要的是(高度)
基本上,似乎/g不再起作用了:运行相同的
不带/g的replace命令生成完全相同的输出:
$ cat data3 | perl -ne "s,(\w+)(\s*) =(['\"])([^\3]*)\3,\1\2=\3\4\3,; print;"
posAndWidth="40:5 =" height ="1"
posAndWidth="-1:8 ='" textAlignment ="Right"
在这个regexp中,似乎忽略了/g。
想知道为什么吗?在替换中插入一些调试字符可以说明这个问题:
use strict;
use warnings;
while (<DATA>) {
s,(\w+)(\s*) =(['"])([^\3]*)\3,$1$2=$3<$4>$3,g;
print; # here -^ -^
}
__DATA__
posAndWidth ="40:5 =" height ="1"
posAndWidth ="-1:8 ='" textAlignment ="Right"
输出:
posAndWidth="<40:5 =" height ="1>"
posAndWidth="<-1:8 ='" textAlignment ="Right>"
# ^--------- match ---------------^
$VAR1 = [
'posAndWidth',
'="40:5 ="',
'height',
'="1"'
];
posAndWidth ="40:5 =" height ="1"
$VAR1 = [
'posAndWidth',
'="-1:8 =\'"',
'textAlignment',
'="Right"'
];
posAndWidth ="-1:8 ='" textAlignment ="Right"
我包含了转储程序输出,因此您可以看到字符串是如何分割的。我将详细说明我对TLP答案的评论: TTSIODRA您提出了两个问题: 1-为什么您的正则表达式不能产生期望的结果?为什么
g
标志不起作用
答案是因为正则表达式包含未正确处理的此部分:\3
。我找了它,但找不到一个方法在角色类中有一个反向引用
2-如何删除等号前面的空格,而不使用引号后面和引号之间的部分
这将是一种方法(请参阅):
正则表达式的第一部分捕获引号之间的任何内容(单引号或双引号),并替换为匹配项,第二部分对应于前面有您要查找的空格的等号。
请注意此解决方案只是通过使用非贪婪运算符*?
最后,如果您想继续从事以下工作: 方括号中带引号的部分仍然表示
“[\”]”“
,但我必须在整个perl命令周围使用单引号,否则负前瞻(?!…)
语法在bash中返回错误
EDIT更正了带有负前瞻的正则表达式:再次注意非贪婪运算符*?
和g
标志
EDIT考虑了ttsiodras的评论:删除了非贪婪运算符
EDIT考虑了TLP的评论它不是将第一个引号和最后一个引号之间的所有内容都视为一个带引号的字符串吗?除了结束引号,[^\3]*部分不能继续匹配,是吗?使用perl cmd,我得到了不同的结果
posAndWidth=“40:5=“
在5
和=
之间的空格消失了。从每行删除第一个条目时会发生什么情况?那么第二个匹配吗?如果是,你的锚地有问题。如果不是,那就是正则表达式的问题。乍一看,我也看不出它们有什么缺陷。了解Perl反斜杠转义在各种上下文和版本中的含义。简而言之,字符类中1–3位数字前的反斜杠是一个八进制数,因此您的\3
是\cC
或\x03
或\x{0003}
-换句话说,当在字符类中使用时,它是一个Control-C。如果[^\3]*没有做我想做的事情。。。那它到底做什么呢?它应该匹配除我们开头的引号之外的任何字符-因此它应该在第一个结束引号中停止。这是正则表达式引擎中的错误吗?@ttsiodra在字符类中,我怀疑元字符是否有效。在这种情况下,您试图否定\3
,不管它变成什么。您是否尝试过使用“重新调试”?关于负反向引用的讨论:它解释了[^…]
与TLP预期的反向引用不兼容。您答案的第二部分(我开始使用的负反向引用)不起作用-它只删除了第一个等号的空格…perl-ne'。。。打印
是perl-pe'…'的长版本代码>完美,谢谢。不过,为了确保:您添加的SO问题的链接使用((?!\3)。*-您使用(?!\3)。*我不确定您的表单是否正确。。。理论上,您的表单可以匹配一个非引号,然后继续匹配任何内容。已验证-不需要非贪婪星:perl-pe,(\w+
$ cat data3 | perl -pe 's,(\w+)(\s*) =(["'"'"'])((?:(?!\3).)*)\3,\1\2=\3\4\3,g'
posAndWidth="40:5 =" height ="1"
posAndWidth="-1:8 ='" textAlignment="Right"