我正在调查一个正则表达式的谜团。我很累,所以我可能会失踪 显而易见的事 - 但我看不出任何理由。
在下面的示例中,我使用perl - 但我第一次在VIM中看到了这个, 所以我猜这是与多个正则表达式引擎相关的东西。
假设我们有这个文件:
$ 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"
我们使用反向引用\ 2来搜索任何不符合的内容 与我们第一次见到的报价相同的报价,任意次数([^ \ 2] *)。 然后我们搜索原始报价本身(\ 2)。如果找到, 我们使用后引用来引用替换中的匹配部分 目标
现在看看:
$ cat data3
posAndWidth ="40:5 =" height ="1"
posAndWidth ="-1:8 ='" textAlignment ="Right"
我们想要的是删除存在的 last 空格字符 在 all 每行之前的'='实例之前。像以前一样,我们不能使用 一个简单的's,=“,=”,g',因为字符串本身可能包含 等号。
所以我们遵循与上面相同的模式,并使用反向引用:
$ 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"
在这个正则表达式中,似乎忽略了/ g。 有什么想法吗?
答案 0 :(得分:3)
在替换中插入一些调试字符可以解决这个问题:
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 ---------------^
请注意,匹配会同时通过两个引号。似乎[^\3]*
没有做你认为它做的事情。
正则表达式不是这里最好的工具。使用可以处理带引号的字符串的解析器,例如Text::ParseWords
:
use strict;
use warnings;
use Data::Dumper;
use Text::ParseWords;
while (<DATA>) {
chomp;
my @a = quotewords('\s+', 1, $_);
print Dumper \@a;
print "@a\n";
}
__DATA__
posAndWidth ="40:5 =" height ="1"
posAndWidth ="-1:8 ='" textAlignment ="Right"
<强>输出:强>
$VAR1 = [
'posAndWidth',
'="40:5 ="',
'height',
'="1"'
];
posAndWidth ="40:5 =" height ="1"
$VAR1 = [
'posAndWidth',
'="-1:8 =\'"',
'textAlignment',
'="Right"'
];
posAndWidth ="-1:8 ='" textAlignment ="Right"
我包含了Dumper输出,因此您可以看到字符串是如何拆分的。
答案 1 :(得分:1)
我将详细阐述我对TLP答案的评论:
你会问两个问题: 1-为什么你的正则表达式没有产生预期的结果?为什么g
标志不起作用?
答案是因为您的正则表达式包含未正确处理的此部分[^\3]
:\3
未被识别为后向引用。我找了它但找不到在字符类中有后引用的方法。
2-如何删除等号前面的空格并单独留下后面的部分并在引号之间?
这是一种方法(见this reference):
$ cat data3 | perl -pe "s,(([\"']).*?\2)| (=),\1\3,g"
posAndWidth="40:5 =" height ="1"
posAndWidth="-1:8 ='" textAlignment="Right"
正则表达式的第一部分捕获引号之间的任何内容(单引号或双引号)并由匹配替换,第二部分对应于前面带有您要查找的空格的等号。
请注意这个解决方案只是通过使用非贪婪的运算符[^\3]
来围绕具有反向引用*?
的补充字符类运算符的“有趣”部分。 p>
最后,如果你想追求negative lookahead solution:
$ 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"
方括号之间带引号的部分仍然意味着"[\"']"
但我必须在整个perl命令周围使用单引号,否则负向前瞻(?!...)
语法会在bash中返回错误。
编辑使用否定前瞻修正了正则表达式:再次注意非贪婪的运算符*?
和g
标记。
编辑将ttsiodras的评论记入帐户:删除了非贪婪的操作员。
编辑将TLP的评论记入帐户