2013-05-21 60 views
1

我有一個包含具體情況如下文件:Perl的正則表達式提取

 
/var/example/12.1.1.0-gn/product 
/var/example/12.1.1.0-xn/product 
       . 
       . 
/var/example/13.1.1.0-gn/product 
/var/example/13.1.1.0-xn/product 

我想用上面的路徑,並插入新的變量,即:

 
/var/example/12.1.1.0/12.1.1.0-gn/product 
/var/example/12.1.1.0/12.1.1.0-xn/product 
       . 
       . 
/var/example/13.1.1.0/13.1.1.0-gn/product 
/var/example/13.1.1.0/13.1.1.0-xn/product 

我已經寫下面的腳本是:

其中$new_add代表了新的部分增加的部分。我試圖通過正則表達式來推廣該腳本。我是perl的新手,所以如果我在某個地方錯了,請指導我。謝謝。

open (FH) or dir ("Could not open the file"); 
foreach $line (<FH>){ 
    ($a, $b, $c, $d, $e, $f) = split ('/', $line); 
     chomp ($line); 
     print "$a, $b, $c, $d $e $f\n"; 
     if ($e =~ m/^\d.\d.\d.\d-\d+/){ 
      $new_add = $e; 
      print "Match"; 
     } 
} 
+2

您可能需要擺脫句點符號,因爲沒有修飾過,它會匹配任何字符。 –

回答

3

也許以下將是有用的:

use strict; 
use warnings; 

while (<>) { 
    s!(/\d[^-]+)!$1$1!; 
    print; 
} 

用法:perl script.pl inFile [>outFile]

第二,可選的參數指示輸出到文件。

或者作爲oneliner:perl -p -ne 's!(/\d[^-]+)!$1$1!' inFile [>outFile]

輸出你的數據集:

/var/example/12.1.1.0/12.1.1.0-gn/product 
/var/example/12.1.1.0/12.1.1.0-xn/product 
/var/example/13.1.1.0/13.1.1.0-gn/product 
/var/example/13.1.1.0/13.1.1.0-xn/product 
+0

很好打高爾夫球,但是正則表達式對我來說似乎太鬆了。如果在某個地方還有另一個數字怎麼辦?也就是說,這可能是最小的正則表達式,可以用於他提供的輸入。嘗試使用'-p'來消除最後一個'print' – bonsaiviking

+0

@perreal是的,但是你必須將'/'分隔開來才能找到它。這可以在整個生產線上工作,而不會先分裂。 – bonsaiviking

+0

@bonsaiviking - 感謝您提供'-p'建議。已更新oneliner。 – Kenosis

0
use strict; 
use warnings; 

while (my $line = <>){ 
    my (@v) = split ('/', $line); 
    print join(" ", @v), "\n"; 
    if (my ($new_add) = $v[-2] =~ m/([^-]*)/){ 
     print "Match $new_add\n"; 
    } 
} 
4

你的Perl風格是基於Perl的4,採用一些更好的做法會讓你的Perl寫生活變得更輕鬆。首先,快速的解決問題的方法:

#!/usr/bin/perl -np 
use strict; 
use warnings; 
s{/(\d+\.\d+\.\d+\.\d+)-}{/$1/$1-}; 

這將匹配您的4部分版本字符串,捕捉它,並使它在你的目錄路徑的另一個因素。現在,以解決您的腳本,並告訴你一些更好的Perl:

第一,永遠總是總是use strict; use warnings;啓動腳本。這將執行腳本,這是偉大的一些嚴格的解釋,因爲Perl通常會認爲它知道你想要什麼,並盡一切可能避免造成錯誤。最明顯的事情,use strict;確實是力lexical scoping,這意味着你必須用my聲明變量。

所以你的第一行(use strict; use warnings;後):

open (FH) or dir ("Could not open the file"); 

的Perl現在會抱怨一些事情。首先,文件句柄是變量!因此,我們需要聲明它們像這樣:my $fh。堅持小寫變量名稱;它更具可讀性。 Perl中也並不喜歡裸字dir。我覺得你的意思die,這是一個關鍵字:

open my $fh or die "Could not open the file"; 

好了,我們消除了一些不必要的括號,拍行更具可讀性。但是現在該文件永遠無法打開。這是因爲你沒有提供文件名!有兩種使用open的方法很多,但是對於大多數的目的,最好的是3個參數的形式。參數是:文件句柄,模式文件名。在這種情況下,我們需要從文件中讀取,所以模式"<"

open my $fh, "<", "test.txt" or die "Could not open the file"; 

這將是指出,可以通過包括use autodie;在離開錯誤處理學習Perl的好時機腳本的頂部。現在你的腳本是這樣的:

#!/usr/bin/perl 

use strict; 
use warnings; 
use autodie; 

open my $fh, "<", "test.txt"; 

foreach my $line (<$fh>){ 

現在,foreachfor的代名詞,這是我比較喜歡,因爲它可以節省一些打字。 (my),並且鑽石操作員(<>)現在圍繞我們的詞法文件句柄$fh。不幸的是,這會將整個文件拖入內存,這可能會造成問題。如果我們用一個while環代替,則每行存儲,處理和丟棄,因爲我們通過循環:

while (my $line = <$fh>) { 
    ($a, $b, $c, $d, $e, $f) = split ('/', $line); 

現在看看這個!許多變量需要在詞彙範圍內。一種方法是對所有人使用單一的my聲明:my ($a, $b, $c, $d, $e, $f)。更好的主意是注意到我們有一系列相似的項目。這可能會寫得更好:

my @path = split '/', $line; 

在那裏,那很好!現在我不知道你爲什麼決定chomp下一行;它沒有任何意義,因爲在此之後你不使用$line,所以我們將跳過它。下一行必須進行修改,以使用新的@path變量:

print join(", ", @path), "\n"; 

使用join意味着我們不必知道我們有多少元件分開行成。我們也看到(從這個輸出中)@path的第四個元素(索引3)是我們想要匹配的版本字符串,但是正則表達式有點偏離。

if ($path[3] =~ m/^\d.\d.\d.\d-\d+/){ 

這是尋找的一系列任何字符分隔個位數,而隨後更多的數字後「 - 」。你的例子顯示其中的一些應該是多位數,我們應該匹配文字「。」。 (句點,句號)而不是正則表達式「。」 (任何字符),最後一部分可以是字母(「xn」,「gn」等)。這裏有一個正則表達式匹配:

if ($path[3] =~ m/^(\d+\.\d+\.\d+\.\d+)-../){ 

你會發現我們增加了+意味着「一個或多個」和\逃脫.字符。還有一件事,我們添加了分組圓括號()來捕獲版本字符串,與字符串的其餘部分分開,因爲這就是您想要的目錄名稱。這種捕獲將被存儲在$1變量,所以下一行是現在:

my $new_add = $1; 

而且僅此而已。很明顯,您需要完成更多的工作才能完成腳本,但希望我已經爲您提供了一些工具,使您的Perl體驗更好。如果你想要的只是一個快速解決方案,那就是頂端的方法。

如果你想繼續使用Perl進行編程,我建議你寫一本教Perl 5的書,最好是在過去的5到6年中編寫的書。一個我強烈推薦Modern Perl,這也是可用for free online

+1

這是一個美麗和詳細的答案。但是我必須指出,你不應該爲我的$行('<$fh>') - 這創建了文件中所有行的列表,然後遍歷它們。 'while(my $ line = <$fh>)'更合適,因爲它使用''>'運算符作爲迭代器。 chomp實際上很重要,但是應該在* split之前完成。 – amon

+0

@amon非常好的一點!我將編輯我的答案以反映這一點。我做了很多Perl編程已經有一段時間了,所以我太專注於保留原始腳本的流程。 – bonsaiviking

+0

@all非常感謝您的時間和回覆。 – deep