2011-09-10 127 views
10

有沒有辦法做到這一點,而沒有獲得無限循環?perl中的無限while循環

while((my $var) = $string =~ /regexline(.+?)end/g) { 
    print $var; 
} 

這將導致一個無限循環,可能是因爲VAR直接從裏面同時一個正則表達式的分配返回「true」每一次?

我知道我能做到這一點:

while($string =~ /regexline(.+?)end/g) { 
    my $var = $1;  
    print $var; 
} 

,但我希望我能夠挽救一條線。有沒有我可以使用的正則表達式修飾符或類似的東西?

(同樣,這是什麼符號/招居然叫,如果我想尋找它:

(my $var) = $string =~ /regex/; 

謝謝!

回答

8

有沒有辦法做到這一點沒有得到一個無限循環?

是的。使用foreach(),而不是一個while()循環:

foreach my $var ($string =~ /regexline(.+?)end/g) { 

這是什麼符號/招居然叫,如果我想尋找它

這就是所謂的匹配列表上下文。它在「perldoc perlop」中有所描述:

g修飾符指定全局模式匹配 - 即在字符串內匹配儘可能多的次數。它的行爲方式取決於上下文。在列表環境...

+2

被警告,'foreach'將整個結果集加載到內存中,而不是象'while'一樣迭代它。 – hhaamu

10

在標量環境,正則表達式與/g修飾符像一個迭代器,並返回一個錯誤的值時,有沒有更多的匹配:

print "$1\n" while "abacadae" =~ /(a\w)/g;  # produces "ab","ac","ad","ae" 

隨着while表達,Y內部分配你正在列表上下文中評估你的正則表達式。現在你的正則表達式不再像迭代器那樣工作,它只是返回匹配列表。如果該列表不爲空,則計算結果爲真值:

print "$1\n" while() = "abacadae" =~ /(a\w)/g; # infinite "ae" 

爲了解決這個問題,你可以分配出while語句,並使用內置的$1變量,使循環內的分配?

while ($string =~ /regexline(.+?)end/g) { 
    my $var = $1; 
    print $var; 
} 
0

有幾種方法可以用較少的代碼來實現。

比方說,你有一個叫lines.txt文件:

regexlineabcdefend 
regexlineghijkend 
regexlinelmnopend 
regexlineqrstuend 
This line does not match 
Neither does this 
regexlinevwxyzend 

,並要提取匹配您的正則表達式的作品,那就是「regexline」和「結束」之間的界線塊。一個直接的Perl腳本是:

while (<STDIN>) { 
    print "$1\n" if $_ =~ /regexline(.+?)end/ 
} 

運行時這樣

$ perl match.pl < lines.txt 

abcdef 
ghijk 
lmnop 
qrstu 
vwxyz 

你甚至可以做到在命令行整個事情!

$ perl的-Nle '打印$ 1,如果$ _ =〜/regexline(.+?)end/' < lines.txt ABCDEF ghijk lmnop qrstu VWXYZ

至於你的第二個問題去,我不知道這個伎倆的一個特殊的Perl名稱。

0

我認爲你最好的選擇是隻更換循環中的$ string ...所以:

while((my $var) = $string =~ /regexline(.+?)end/g) { 
    $string =~ s/$var//; 
    print $var . "\n"; 
} 
+0

我建議你嘗試運行該代碼;它有語法錯誤。 'print $ var。 「\ n」;'沒問題,但'print'$ var \ n「;'更清潔。你不需要在'$ string'上做另一個替換;你可以捕獲初始正則表達式中的新值,並將其賦值給'$ string'。 –

8

Perl regular expressions tutorial說:

在標量環境,對一個字符串連續調用將有//從匹配匹配摹跳,跟蹤的位置在字符串中,因爲它沿着去。

但是:

在列表環境,// g ^返回匹配的分組列表,或者如果沒有分組,整個正則表達式匹配的列表。

也就是說,在//g返回一次所有拍攝比賽的數組(其中您隨後丟棄所有,但第一批)名單上下文,然後這是否一遍您的每一次循環的執行(即永遠)。

所以你不能在循環條件中使用列表上下文分配,因爲它沒有做你想做的事。

如果你堅持使用列表範圍內,你可以這樣做,而不是:

foreach my $var ($string =~ /regexline(.+?)end/g) { 
    print $var; 
} 
0

我不知道你打算用這個打印要做什麼,但是這是做的一個很好的方法:

say for $string =~ /regex(.+?)end/g; 

的對(同的foreach)將正則表達式匹配展開成捕獲組的列表,並將其打印出來。這樣的工作:

@matches = $string =~ /regex(.+?)end/g; 
say for (@matches); 

while有所不同。由於它使用標量上下文,因此它不會將捕獲組加載到內存中。

say $1 while $string =~ /regex(.+?)end/g; 

它會像做你的原代碼,除了我們並不需要使用轉換變量$var,我們只是打印出來的時候了。

1

這是你無法避免使用全局變量而不改變行爲的情況。

while ($string =~ /regexline(.+?)end/g) { 
    my $var = $1; 
    ... 
} 

如果您只有一個捕獲,您可以通過一次查找所有匹配來避免使用全局變量。

for my $var ($string =~ /regexline(.+?)end/g) { 
    ... 
} 

第二個版本的額外費用通常可以忽略不計。