2011-11-17 28 views
2

垂直分隔解析的文件我有一個看起來像這樣的文件:在Perl

*NEWRECORD 
RECTYPE = D 
MH = Calcimycin 
AQ = AA 
MED = *62 

*NEWRECORD 
RECTYPE = D 
MH = Urinary Bladder 
AQ = AB AH BS CH CY DE EM EN GD IM IN IR ME MI PA PH PP PS RA RE RI SE SU TR UL US VI 
CX = consider also terms at CYST- and VESIC- 
MED = *1359 

每個記錄塊都有不同的行數,(例如CX條目並不總是存在)。 但是,如果CX存在,則僅顯示爲1條目。 我們希望獲得一個以「MH」作爲鍵和「CX」作爲值的哈希表。

因此解析,我們希望得到這個結構上面的數據:

$VAR = { "Urinary Bladder" => ["CYST-" , "VESIC-"]}; 

什麼是分析它的正確方法?

我堅持這一點,那不會給我結果,因爲我想要的。

use Data::Dumper; 
my %bighash; 
my $key = ""; 
my $cx = ""; 
while (<>) { 

    chomp; 

    if (/^MH = (\w+/)) { 

     $key = $1;  
     push @{$bighash{$key}}, " "; 
    } 
    elsif (/^CX = (\w+/)) { 
     $cx = $1; 

    } 
    else { 
     push @{$bighash{$key}}, $cx; 

    } 

} 

回答

5

如果使用$/讀取數據,以一定的時間段落這變得更簡單。我很驚訝,沒有其他人提出這個建議。

#!/usr/bin/perl 

use strict; 
use warnings; 
use 5.010; 

use Data::Dumper; 

my %bighash; 

$/ = ''; 

while (<DATA>) { 
    if (my ($k) = /^MH = (.*?)$/m and my ($v) = /^CX = (.*?)$/m) { 
    $bighash{$k} = [ $v =~ /([A-Z]+-)/g ]; 
    } 
} 

say Dumper \%bighash; 

__DATA__ 
*NEWRECORD 
RECTYPE = D 
MH = Calcimycin 
AQ = AA 
MED = *62 

*NEWRECORD 
RECTYPE = D 
MH = Urinary Bladder 
AQ = AB AH BS CH CY DE EM EN GD IM IN IR ME MI PA PH PP PS RA RE RI SE SU TR UL US VI 
CX = consider also terms at CYST- and VESIC- 
MED = *1359 

輸出看起來是這樣的:

$VAR1 = { 
      'Urinary Bladder' => [ 
           'CYST-', 
           'VESIC-' 
           ] 
     }; 
1

最近我還沒有練習過我的perl功夫,但最後的陳述看起來很腥。

嘗試刪除最後一個else語句,並在第二個elsif之後直接添加'push'語句。基本上在匹配CX後直接進行推壓操作。

另外,您知道MH必須始終出現在CX之前,否則邏輯會中斷。

1
  • 修復正則表達式 /^MH = (\w+/)應該是/^MH (\w+)/。您可能需要使用鑰匙$關鍵
  • 列表項
  • 使用 \s+\s*代替空間從 if
  • 刪除推刪除else
  • elsif塊推$ CX成散列
  • 添加use strict;use warnings;到您的代碼

嘗試這些,如果你有困難我將幫助你的代碼

3

請嘗試以下操作。它可能是一個好主意,檢查的變化(或聽阿貴):

use strict; 
use warnings; 

use Data::Dumper; 

my %bighash; 
my $current_key; 

while (<DATA>) { 

    chomp; 

    if (m/^MH = (.+)/) { 
     $current_key = $1; 

    } elsif (/^CX = (.+)/) { 
     my $text = $1; 
     $bighash{ $current_key } = [ $text =~ /([A-Z]+-)/g ]; 

    } 
} 

print Dumper (\%bighash); 

__DATA__ 
*NEWRECORD 
RECTYPE = D 
MH = Calcimycin 
AQ = AA 
MED = *62 

*NEWRECORD 
RECTYPE = D 
MH = Urinary Bladder 
AQ = AB AH BS CH CY DE EM EN GD IM IN IR ME MI PA PH PP PS RA RE RI SE SU TR UL US VI 
CX = consider also terms at CYST- and VESIC- 
MED = *1359 

更新:使用正則表達式,捕獲,而不是splitgrep

1

這可能是簡單的使用Config::TinyConfig::YAML做首先傳遞文件,然後單獨循環遍歷每條記錄。雖然如果你的文件像千兆字節或更多,這可能會佔用你所有的內存。

1

這裏是我很快做了,我希望它給你一個想法從開始:

use Data::Dumper; 
# Set your record separator 
{ 
    local $/="*NEWRECORD\n"; 

    while(<DATA>) { 
    # Get rid of your separator 
    chomp($_); 
    print "Parsing record # $.\n"; 
    push @records, $_ if ($_); 
    } 
} 


foreach (@records) { 
    # Get your sub records 
    @lines = split(/\n/,$_); 
    my %h =(); 
    my %result =(); 
    # Create a hash from your sub records 
    foreach (@lines) { 
    ($k, $v) = split(/\s*=\s*/, $_); 
    $h{$k} = $v; 
    } 
    # Parse the CX and strip the lower case comments 
    $h{ 'CX' } =~ s/[a-z]//g; 
    $h{ 'CX' } =~ s/^\s+//g; 
    # Have the upper case values as an array ref in the result hash 
    $result{ $h{ 'MH' } } = [ split(/\s+/, $h{ 'CX' }) ] if ($h{ 'CX' }); 
    print Dumper(\%h); 
    print "Result:\n"; 
    print Dumper(\%result); 
} 
__DATA__ 
*NEWRECORD 
RECTYPE = D 
MH = Calcimycin 
AQ = AA 
MED = *62 

*NEWRECORD 
RECTYPE = D 
MH = Urinary Bladder 
AQ = AB AH BS CH CY DE EM EN GD IM IN IR ME MI PA PH PP PS RA RE RI SE SU TR UL US VI 
CX = consider also terms at CYST- and VESIC- 
MED = *1359