【问题标题】:align string to a pattern in perl?将字符串与perl中的模式对齐?
【发布时间】:2011-11-17 13:48:33
【问题描述】:

我在方括号中有大量字符串,如下所示:

[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;

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

【问题讨论】:

  • 我看不出问题出在哪里。你想要什么在 [] 里面还是外面? [] 是否有可能包含 [] ?
  • 您没有定义问题:方括号内有文字, 方括号内有文字。那是一个混合而不是一个问题。 Unix shell 有一个类似的“问题”,有些短语是命令,有些是开关,有些是参数。如果你想把外面的文字放在方括号里,你想给它什么p值?

标签: regex string perl


【解决方案1】:
#!/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 p\d in front of them
my @items = split(/(?<!p\d)\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]

我注意到您已经获得了所需的帮助,但我认为我可以回答您的问题...

【讨论】:

  • 谢谢!我理解你的大部分代码。这行得通。但是,PP 不是唯一的选择,选择取决于标签值。我可以在地图块中的 : 之后以某种方式添加条件语句或处理语句吗?像: map { ($_ =~ m/\[/) ? $_ : @bits = split("/", $_); if($bits[1] eq "IN"){$_ = "[PP $_]";} else {$_ = "[BLA $_]";} } @items;
  • 好的 - 这里有问题。我正在尝试编辑初始拆分语句: my @items = split(/(?
  • @user961627 我编辑了 split 使用的正则表达式,并相应地更改了其余代码。
  • 是的,尽管得到了帮助,但我确实想要理解,以便更好地理解正则表达式,谢谢。
【解决方案2】:

其实你可以用 "only" 正则表达式来解决这个问题:

#!/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] [...]

【讨论】:

  • 哇,我不知道正则表达式可以做到这一点。不过有一个问题(我还是 perl 的新手)——我怎样才能使它适用于声明的变量?而不是 $_ = ... 我只是使用了 $string,但是正则表达式不适用于它。
  • @user961627 只需将 $_ 替换为 my$string = ...,然后将替换正则表达式应用于它,如 $string =~ s{...
  • 您介意解释一下这是什么意思吗? ([^\s[]+) | ([(?:[^[]*)]) 我检查了正则表达式教程,但不太明白“?:”是“被动”或“非捕获”组的含义。此外,这是否可以扩展到不仅可以拾取带有或不带有方括号的短语,还可以拾取只有一个括号的短语?例如。 [p text/label] p text/label] [p text/label] 所以你可以看到中间那个少了一个括号。
  • 好的,我明白第一部分是什么意思:([^\s[]+)|([(?:[^[]*)]) 但我不明白:$1 = ~ m!(.*(?
  • @user961627 这意味着基本上匹配 / 之前的任何内容,也匹配它之后的任何内容。如果你愿意,我可以详细说明。关于您缺少的括号,我的解决方案以及可能的任何其他解决方案在这种情况下都不起作用。
【解决方案3】:

您尚未共享您的正则表达式,但您应该使用 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]

【讨论】:

    猜你喜欢
    • 2017-04-20
    • 2011-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多