2008-12-31 47 views
3

提前祝大家新年好。如何從Perl中的文件中讀取自定義的模式?

我有一個模式參數,結果和stderr(stderr可以是多行)內容的錯誤日誌文件。

$cat error_log 

<parameter>:test_tot_count 
<result>:1 
<stderr>:Expected "test_tot_count=2" and the actual value is 3 
test_tot_count = 3 
<parameter>:test_one_count 
<result>:0 
<stderr>:Expected "test_one_count=2" and the actual value is 0 
test_one_count = 0 
<parameter>:test_two_count 
<result>:4 
<stderr>:Expected "test_two_count=2" and the actual value is 4 
test_two_count = 4 
... 

我需要在Perl中編寫一個函數來將每個參數,結果和stderr存儲在數組或散列表中。

這是我們自己內部定義的結構。我這樣寫了Perl函數。使用正則表達式本身是否有更好的方法?

my $err_msg = ""; 
while (<ERR_LOG>) 
{ 
    if (/<parameter>:/) 
    { 
     s/<parameter>://; 
     push @parameter, $_; 
    } 
    elsif (/<result>:/) 
    { 
     s/<result>://; 
     push @result, $_; 
    } 
    elsif (/<stderr>:/) 
    { 
     if (length($err_msg) > 0) 
     { 
      push @stderr, $err_msg; 
     } 
     s/<stderr>://; 
     $err_msg = $_; 
    } 
    else 
    { 
     $err_msg .= $_; 
    } 
} 
if (length($err_msg) > 0) 
{ 
    push @stderr, $err_msg; 
} 

回答

4

如果你正在使用Perl 5.10,您可以通過使用給定/何時結構做的非常相似,你現在有什麼,但有一個更加美好的佈局東西:

use 5.010; 

while (<ERR_LOG>) { 
    chomp; 
    given ($_) { 
     when (m{^<parameter>: (.*)}x) { push @parameter, $1  } 
     when (m{^<result>: (.*)}x) { push @result, $1  } 
     when (m{^<stderr>: (.*)}x) { push @stderr, $1  } 
     default       { $stderr[-1] .= "\n$_" } 
    } 
} 

值得一提的是,這裏默認的情況下,而不是保持一個獨立的$ ERR_MSG變量,當我看到一個時,我只是推動標記,如果看到延續線,則追加到@stderr數組的最後一項。我在看到延續線時添加了換行符,因爲我假設您希望它們保留。

儘管上面的代碼看上去很優雅,我不是真的所有喜歡保持三個獨立的陣列中,因爲它可能會導致你的頭痛,如果事情不同步,因爲如果你想添加更多在將來你會得到很多很多變量,你需要跟蹤。我建議每個存儲記錄的哈希裏面,然後保存一組記錄:

use 5.010; 

my @records; 

my $prev_key; 

while (<ERR_LOG>) { 
    chomp; 
    given ($_) { 
     when (m{^<parameter> }x) { push(@records, {}); continue;   } 
     when (m{^<(\w+)>: (.*)}x) { $records[-1]{$1} = $2; $prev_key = $1; } 
     default      { $records[-1]{$prev_key} .= "\n$_";  } 
    } 
} 

在這裏,我們正在推動一個新的記錄到,當我們看到一個字段的數組,加入到我們的哈希每當進入我們看到一個鍵/值對,如果看到延續線,則會追加到我們添加的最後一個字段。的@records最後的結果是這樣的:

(
    { 
     parameter => 'test_one_count', 
     result => 0, 
     stderr => qq{Expected "test_one_count=2" and the actual value is 0\ntest_one_count=0}, 
    }, 
    { 
     parameter => 'test_two_count', 
     result => 4, 
     stderr => qq{Expected "test_two_count=2" and the actual value is 4\ntest_two_count=4}, 
    } 
) 

現在你可以通過只是一個單一的數據結構,圍繞着包含所有的記錄,並且可以在未來增加更多的領域(甚至多線的)和他們會被正確處理。

如果你不使用Perl 5。10,那麼這可能是升級的好藉口。如果沒有,您可以將給定/時間結構轉換爲更傳統的if/elsif/else結構,但是在轉換中失去了很多美感。

保羅

+0

我喜歡的第一個。這是一個很好很緊的重構。 +1 – Axeman 2008-12-31 18:33:40

1

看起來不錯。 =)一種改進可能是將這些標籤固定在行首:

if (/^<parameter>:/) 

它會使腳本更健壯一些。

您也可避免標籤的剝離,如果你趕上後有什麼,並且只使用部分:

if (/^<parameter>:(.*)/s) 
{ 
    push @parameter, $1; 
} 
+0

謝謝,我錯過了驗證:-) – 2008-12-31 11:42:02

3

是跳出了重構的主要問題是在匹配的重複,剝離和存儲。像這樣的(未經測試)代碼的東西更簡潔:

my($err_msg , %data); 

while (<ERR_LOG>) { 
    if((my $key) = $_ =~ s/^<(parameter|result|stderr)>://) { 
    if($key eq 'stderr') { 
     push @{ $data{$key} } , $err_msg if $err_msg; 
     $err_msg = $_; 
    } 
    else { push @{ $data{$key} } , $_ } 
    } 
    else { $err_msg .= $_ } 
} 

# grab the last err_msg out of the hopper 
push @{ $data{stderr} } , $err_msg; 

...但它可能是很難理解從現在起6個月〜8 ^)

相關問題