2009-08-16 70 views
2

我需要一些Perl正則表達式幫助。下面的代碼片段:Perl regex如何重新使用前一場比賽的一部分進行下一場比賽?

use strict; 
use warnings; 
my $str = "In this example, A plus B equals C, D plus E plus F equals G and H plus I plus J plus K equals L"; 
my $word = "plus"; 
my @results =(); 
1 while $str =~ s/(.{2}\b$word\b.{2})/push(@results,"$1\n")/e; 
print @results; 

產生以下輸出:

 
A plus B 
D plus E 
2 plus F 
H plus I 
4 plus J 
5 plus K 

我希望看到的是這樣的,一個角色已經匹配可以在新的比賽在不同的上下文中出現:

 
A plus B 
D plus E 
E plus F 
H plus I 
I plus J 
J plus K 

如何更改正則表達式以獲得此結果?感謝---丹

+0

感謝Greg,Sinan和Michael的快速反應。完全披露:引用的例子是一種簡化,$ str實際上是來自產品用戶手冊的500k字符的文本,$ word是可能的單詞匹配的長列表中的一個元素,{2}實際上是{35}並且可以捕獲任何字符在文本中的「加號」附近找到,以便構建文檔中使用「加號」的完整一致性,因此獲得最快性能的分數--- Dan – dlw 2009-08-16 03:52:21

回答

6

一般的建議:不要使用s///當你想要m//。在你的搭配中具體。

答案是pos

#!/usr/bin/perl -l 

use strict; 
use warnings; 

my $str = 'In this example, ' . 'A plus B equals C, ' . 
      'D plus E plus F equals G ' . 
      'and H plus I plus J plus K equals L'; 

my $word = "plus"; 

my @results; 

while ($str =~ /([A-Z] $word [A-Z])/g) { 
    push @results, $1; 
    pos($str) -= 1; 
} 

print "'$_'" for @results; 

輸出:

 
C:\Temp> b 
'A plus B' 
'D plus E' 
'E plus F' 
'H plus I' 
'I plus J' 
'J plus K' 
+1

啊,本質上是相同的答案,但是您清理了正則表達式太。 – 2009-08-16 03:00:46

+1

'pos'只比'substr'感覺更乾淨。 – 2009-08-16 03:04:05

+0

對於'pos'爲+1。不知道那個。 – 2009-08-16 04:11:57

0

下面是做這件事:

use strict; 
use warnings; 
my $str = "In this example, A plus B equals C, D plus E plus F equals G and H plus I plus J plus K equals L"; 
my $word = "plus"; 
my @results =(); 
my $i = 0; 
while (substr($str, $i) =~ /(.{2}\b$word\b.{2})/) { 
    push @results, "$1\n"; 
    $i += $-[0] + 1; 
} 
print @results; 

這不是可怕的Perl的十歲上下,但它的工作原理,並沒有使用太多晦澀的正則表達式的技巧。但是,您可能需要在perlvar中查找特殊變量@-的功能。

3

您可以使用一個m//g代替s///並分配給pos函數的第二個任期之前倒帶比賽地點:

use strict; 
use warnings; 

my $str = 'In this example, A plus B equals C, D plus E plus F equals G and H plus I plus J plus K equals L'; 
my $word = 'plus'; 
my @results; 

while ($str =~ /(.{2}\b$word\b(.{2}))/g) { 
    push @results, "$1\n"; 
    pos $str -= length $2; 
} 
print @results; 
0

不必使用正則表達式。基本上,只是分割字符串,使用循環遍歷每個項目,檢查「加號」,然後從前後獲取單詞。

my $str = "In this example, A plus B equals C, D plus E plus F equals G and H plus I plus J plus K equals L"; 
@s = split /\s+/,$str; 
for($i=0;$i<=scalar @s;$i++){ 
    if ("$s[$i]" eq "plus"){ 
     print "$s[$i-1] plus $s[$i+1]\n"; 
    } 
} 
1

給予 「充分披露」 的評論(但假設.{0,35},不.{35}),我會做

use List::Util qw/max min/; 
my $context = 35; 
while ($str =~ /\b$word\b/g) { 
    my $pre = substr($str, max(0, $-[0] - $context), min($-[0], $context)); 
    my $post = substr($str, $+[0], $context); 
    my $match = substr($str, $-[0], $+[0] - $-[0]); 
    $pre =~ s/.*\n//s; 
    $post =~ s/\n.*//s; 
    push @results, "$pre$match$post"; 
} 
print for @results; 

你會跳過替換,如果你真的意味着(?s:.{0,35})

2

另一種選擇是使用前瞻:

use strict; 
use warnings; 
my $str = "In this example, A plus B equals C, D plus E " 
     . "plus F equals G and H plus I plus J plus K equals L"; 
my $word = "plus"; 
my $chars = 2; 
my @results =(); 

push @results, $1 
    while $str =~ /(?=((.{0,$chars}?\b$word\b).{0,$chars}))\2/g; 

print "'$_'\n" for @results; 

在先行,拍攝組可變數量的開頭和結尾的背景人物,到什麼最大您所設置沿1個匹配的單詞。當前瞻結束時,反向引用\2匹配「真實的」,不管是否被第2組捕獲,除了停止在單詞的末尾之外,這與第1組相同。這會在您想要的位置設置pos,而無需計算您在該單詞之後實際匹配了多少個字符。

+0

感謝發佈,我學到了更多關於正則表達式的知識。我想知道,這是更快的解決方案還是使用pos()的Sinan? – dlw 2009-08-18 02:56:19

+0

他們並不完全等同。思南的代碼基於你原來的問題,在任何一端都恰好匹配2個額外的字符,並且將'pos'正好顛倒一個位置。 Mine允許使用可變數量的上下文字符(在這種情況下,2是最大值),在閱讀完整的信息披露評論後,這看起來更爲現實。我的解決方案可以比ysth更有用,我希望他的速度更快,因爲它可以讓正則表達式引擎找到'\ b $ word \ b'的匹配,而不會在其中添加不情願的量詞。 – 2009-08-18 13:00:24