解析文件在Perl中垂直分隔

时间:2011-11-17 08:46:03

标签: perl parsing

我有一个看起来像这样的文件:

*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个条目。 我们希望得到一个Hash,它将“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;

   }

} 

6 个答案:

答案 0 :(得分: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 :(得分:3)

尝试以下方法。检查变化(或听Aki)可能是个好主意:

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

更新:使用Regex-Captures代替splitgrep

答案 2 :(得分:1)

最近没有练过我的perl功夫,但最后一句话看起来很可疑。

尝试删除最后一个else语句并在第二个elsif之后直接添加'push'语句。基本上在匹配CX后直接进行推动操作。

另外,您知道MH必须始终出现在CX之前,否则逻辑会中断。

答案 3 :(得分:1)

  • 修复正则表达式 /^MH = (\w+/)应为/^MH (\w+)/。您可能希望使用\s+\s*代替空格
  • if
  • 中删除推送
  • 删除else阻止
  • elsif块中使用密钥$ key
  • 将$ cx推入哈希
  • 列表项
  • use strict;use warnings;添加到您的代码

尝试这些,如果您遇到困难,我会帮助您解决代码

答案 4 :(得分:1)

使用Config::TinyConfig::YAML对文件进行初始传递然后单独循环遍历每个记录可能更简单。虽然如果你的文件像一个千兆字节或更多,这可能会占用你所有的记忆。

答案 5 :(得分: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
相关问题