我使用以下代码来匹配字符串(EX: <jdgdt\s+mdy=.*?>\s*)
,而不应该跟随另一个特定字符串(<jdg>)
。但是我无法按照以下代码获得所需的输出。任何人都可以帮我这个吗?
输入文件:
<dckt>Docket No. 7677-12.</dckt>
<jdgdt mdy='02/25/2014'>
<jdg>Opinion by Marvel, <e>J.</e></jdg>
<taxyr></taxyr>
<disp></disp>
</tcpar>
<dckt>Docket No. 7237-13.</dckt>
<jdgdt mdy='02/24/2014'>
</tcpar>
期望的输出:
<dckt>Docket No. 7677-12.</dckt>
<jdgdt mdy='02/25/2014'>
<jdg>Opinion by Marvel, <e>J.</e></jdg>
<taxyr></taxyr>
<disp></disp>
</tcpar>
<dckt>Docket No. 7237-13.</dckt>
<jdgdt mdy='02/24/2014'>
<jdg>Opinion by Marvel, <e>J.</e></jdg>
<taxyr></taxyr>
<disp></disp>
</tcpar>
代码:
#/usr/bin/perl
my $filename = $ARGV[0];
my $ext = $ARGV[1];
my $InputFile = "$filename" . "\." . "$ext";
my $document = do {
local $/ = undef;
open my $fh, "<", $InputFile or die "Error: Could Not Open File $InputFile: $!";
<$fh>;
};
$document =~ s/(<jdgdt\s+mdy=.*?>\s*)(?!<jdg>)/$1<jdg>Opinion by Marvel,<e>J.<\/e><\/jdg>\n<taxyr><\/taxyr>\n<disp><\/disp>/isg;
print $document;
答案 0 :(得分:3)
我必须对你的正则表达式进行两次小调整才能获得所需的输出:
$document =~ s{(<jdgdt\s+mdy\=[^>]*>\s*)(?!\s*<jdg>)}{$1<jdg>Opinion by Marvel,<e>J.</e></jdg>\n<taxyr></taxyr>\n<disp></disp>}isg;
另外,为了清理代码,我使用/
切换到使用{}
来分隔正则表达式;这样,你不需要反击你替换中实际需要的所有斜杠。
解释我改变了什么:
首先,否定前瞻是棘手的。你必须要记住的是,perl会尝试将你的表达与最大可能的次数相匹配。因为你最初有这个:
/(<jdgdt\s+mdy\=.*?>\s*)(?!<jdg>)/
在第一个条款中,您将获得此匹配:
<jdgdt mdy='02/25/2014'>\n<jdg>Opinion by Marvel, <e>J.</e></jdg>
^^^^^^^^^^^^^^^^^^^^^^^^
(this part matched by paren. Note the \n is not matched!)
Perl认为这是一个匹配,因为在第一个带括号的表达式之后,你有&#34; \n<jdg>
&#34;。嗯,这与表达式&#34; <jdg>
&#34;不匹配。 (因为最初的换行),所以耶!发现了一场比赛。
换句话说,最初,perl会使\s*
结束你的括号表达式并匹配空字符串,因此它会找到一个匹配项,你最终会把东西塞进第一个你不想要的条款。另一种说法是,由于可以自由选择进入\s*
的内容,perl会选择允许整个表达式匹配的数量。 (并使用第一个文档记录的空字符串填充\s*
,并为第二个文档记录添加换行符)
为了让perl永远不会在第一个文档记录中找到匹配项,我还在负面预测中重复了\s*
。这样,没有选择放入\s*
的内容可以使表达式在初始文档记录中完全匹配,并且perl必须放弃并移动到第二个文档记录。
但是还有第二个问题!还记得我是怎么说perl在任何地方找到匹配都非常积极吗?好吧,接下来perl会扩展你的mdy\=.*?>
位,以便在第一个docket记录中找到结果。在我向负向前瞻添加\s*
之后,第一个文档仍在匹配(但在不同的位置):
<jdgdt mdy='02/25/2014'>\n<jdg>Opinion by Marvel, <e>J.</e></jdg>
^^^^^^^^^^^???????????????????^
(Underlined part matched by paren. ? denotes the bit matched by .*?)
了解perl如何将.*?
方式扩展到您的预期之外?您希望该位仅匹配第一个>
字符的内容,但perl会根据需要扩展您的非贪婪匹配,以便整个模式匹配。这一次,它延长了.*?
以覆盖关闭>
标记的<jdg>
,以便它可以找到负向前瞻不会阻止匹配的位置。
为了防止perl延伸到.*?
模式,我将.*?
替换为[^>]*
,这就是你的意思。
在这两次更改之后,我们只在最初需要的第二个文档记录中找到匹配。
答案 1 :(得分:-2)
使用正向前瞻。 (?!<jdg>)
或类似的东西,查找它。