在 Perl 中将字符串与模式对齐



我在方括号内有字符串块,如下所示:

[p1 text1/label1] [p2 text2/label2] [p3 text3/label3] [...

等等。

每个块中的内容并不重要。但有时有些杂散的文本块没有被方括号包围。例如:

[p1 text1/label1] [p2 text2/label2] textX/labelX  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]

以为我在perl中使用正则表达式解决了这个问题,直到我意识到我只满足了在文本的开头,中间或结尾有一个杂散文本的情况,而不是我们可能有两个杂散的情况在一起。(如上面的 Y 和 Z 块(。

所以我意识到 perl 中的正则表达式只捕获第一个匹配模式?那么上述问题该如何解决呢?

编辑:

问题是确保所有内容都应用括号括起来。方括号从不递归。当用括号将短语括起来时,p 值取决于"标签"值。例如,如果一个杂散的无括号短语是

li/IN

然后它应该变成:

[PP li/IN]

我想这是一个混合,但我能想到解决我正在处理的更大问题的唯一方法是将它们全部转换为括号短语,这样处理起来更容易。因此,如果一个不带括号的短语出现在开头、中间和结尾,我就可以工作,但如果两个或更多短语一起发生,则不会。

我基本上为每个位置(开始、中间和结束(使用了不同的正则表达式。在中间捕获一个不带括号的短语的那个看起来像这样:

$data =~ s/] (text)#/label [/] [selected-p-value $1#/label] [/g;

所以我所做的只是注意到,如果 ] 在文本/标签模式之前和之后出现,那么这个没有括号。我也为其他人做类似的事情。但我想这是非常不通用的。我的正则表达式不是很好!

#!/usr/bin/perl
use strict;
use warnings;
my $string = "[p1 text1/label1] [p2 text2/label2] textX/labelX  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]";
# don't split inside the [], i.e. not at blanks that have pd in front of them
my @items = split(/(?<!pd)s+/, $string);
my @new_items;
# modify the items that are not inside []
@new_items = map { ($_ =~ m/[/) ? $_ :
                    ((split("/",$_))[1] eq ("IN")) ? "[PP $_]" :
                    "[BLA $_]";
                 } @items;
print join(' ', @new_items), "n";

这给了

[p1 text1/label1] [p2 text2/label2] [PP textX/labelX] [p3 text3/label3] [...] [PP textY/labelY] [PP textZ/labelZ] [...]

我认为PP的意思是我在这里使用它,否则map将不得不变得更加复杂。

编辑

我已经编辑了代码以响应您的评论。如果您使用

"[p1 text1/label1] [p2 text2/label2] textX/IN  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]";

作为示例字符串,这是输出:

[p1 text1/label1] [p2 text2/label2] [PP textX/IN] [p3 text3/label3] [...] [BLA textY/labelY] [BLA textZ/labelZ] [...]

只有一件事要记住:与 split 一起使用的正则表达式不适用于 n> 9 的pn。如果你有这种情况,最好寻找一个替代方案,因为可变长度的回溯还没有实现(或者至少在我的Perl版本(5.10.1(中没有(。

编辑 2

作为对您的第二条评论的回复,这里是脚本的修改版本。您会发现我还在示例字符串中添加了一些内容,以证明即使[...]内部没有pn,它现在也可以工作。

#!/usr/bin/perl
use strict;
use warnings;
my $string = "[p1 text1/label1] [p2 text2/label2] textX/IN  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...] xyx/IN [opq rs/abc]";
# we're using a non-greedy match to only capture the contents of one set of [], 
# otherwise we'd simply match everything between the first [ and the last ].
# The parentheses around the match ensure that our delimiter is KEPT.
my @items = split(/([.+?])/, $string);
#print "..$_--n" for @items;  # uncomment this to see what the split result looks like
# modify the items that are not inside []
my @new_items = map {
                     if (/^[/) { # items in []
                        $_;
                     }
                     elsif (/(?: w)|(?:w )/) { # an arbitrary number of items without []
                       my @new =  map { ($_ =~ m/[/) ? $_ :
                                        ((split("/",$_))[1] eq ("IN")) ? "[PP $_]" :
                                        "[BLA $_]";
                                      } split;
                     }
                     else { # some items are '', let's just discard those
                     }
                    } @items;
print join(' ', @new_items), "n";

输出是这样的:

[p1 text1/label1] [p2 text2/label2] [PP textX/IN] [p3 text3/label3] [...] [BLA textY/labelY] [BLA textZ/labelZ] [...] [PP xyx/IN] [opq rs/abc]

注意到你已经得到了你需要的帮助,但我想我仍然可以回答你的问题......

实际上您可以使用"仅">正则表达式来解决此问题:

#!/usr/bin/perl
use strict;
use warnings;
$_ = "[p1 text1/label1] [p2 text2/label2] textX/labelX  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]";
s{ ([^s[]+)|([(?:[^[]*)])     }
 { if( defined $2){ $2 } elsif(defined $1)
    { 
       if($1 =~ m!(.*(?<=/)(.*))!)
       {
         if($2 eq 'labelX')
         {
            "[PP $1]";
         }
         elsif($2 eq 'labelY')
         {
            "[BLA $1]";
         }
         elsif($2 eq 'labelZ')
         {
            "[FOO $1]";
         }
       }
    }
 }xge;
 print;

输出:

[p1 text1/label1] [p2 text2/label2] [PP textX/labelX]  [p3 text3/label3] [...] [BLA textY/labelY] [FOO textZ/labelZ] [...]

您尚未共享正则表达式,但应使用 g 进行全局替换。否则 perl 正则表达式仅替换第一个匹配项

my $teststring = "hello world";
$teststring =~ s/o/X/;

会变得hellX world.

$teststring =~ s/o/X/g;

将变得hellX wXrld注意到所有比赛。

我认为你的问题是这样的

my $teststring = ' A B C ';
$teststring =~ s/s(w)s/ [$1] /ig;

产量[A] B [C] . 它没有做 B,原因是作为匹配 A 的一部分,正则表达式机器也消耗了 A 之后的空间。在剩余的字符串中,B 之前没有空格,因此不匹配。

但是如果你像这样做一个非贪婪的比赛

$teststring =~ s/s(w)s*?/ [$1] /ig;

它产生[A] [B] [C]

最新更新