Разбор файла с разделителями по вертикали в Perl - PullRequest
2 голосов
/ 17 ноября 2011

У меня есть файл, который выглядит так:

*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;

   }

} 

Ответы [ 6 ]

5 голосов
/ 17 ноября 2011

Это становится проще, если вы используете $/ для чтения данных абзаца за раз. Я удивлен, что никто другой не предложил это.

#!/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-'
                               ]
        };
3 голосов
/ 17 ноября 2011

Попробуйте следующее. И, вероятно, это хорошая идея, чтобы изучить изменения (или послушать Аки):

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 вместо split и grep

1 голос
/ 17 ноября 2011

Вот кое-что, что я быстро сделал, надеюсь, это даст вам идею начать с:

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
1 голос
/ 17 ноября 2011

Может быть проще использовать Config :: Tiny или Config :: YAML , чтобы выполнить первоначальный проход по файлу, а затем выполнить цикл по каждой записи в отдельности.Хотя, если ваш файл похож на гигабайт или больше, это может высосать всю вашу память.

1 голос
/ 17 ноября 2011
  • Исправить регулярные выражения /^MH = (\w+/) должно быть /^MH (\w+)/.Вы можете захотеть использовать \s+ или \s* вместо пробела
  • Удалить толчок из блока if
  • Удалить else блок
  • В elsif block Вставьте $ cx в хеш с помощью клавиши $ key
  • Элемент списка
  • Добавьте use strict; и use warnings; к своему коду

Попробуйте эти и еслиу вас возникли трудности, я помогу вам с кодом

1 голос
/ 17 ноября 2011

В последнее время я не практиковал моё perl kung fu, но последнее выражение else выглядит подозрительно.

Попробуйте отбросить последнее выражение else и добавить выражение "push" сразу после второго elsif.Обычно выполняйте операцию push сразу после сопоставления с CX.

Кроме того, вы знаете, что MH всегда должен появляться перед CX, в противном случае логика прерывается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...