Perl — захват и замена нескольких совпадений в операторе If

Я разрабатывал сценарий, чтобы взять некоторый вывод из пакета IBM SPSS Statistics и преобразовать его в синтаксис ввода SPSS. В настоящее время у меня есть следующая проблема, которую я не могу понять. У меня есть текст, который выглядит следующим образом:

VALUE LABELS V10
-1
 1 "Yes".

VALUE LABELS V11
-50.00
-33.33
 -10 "Don't Know".

Я хочу заменить отрицательные значения, за которыми не следуют метки в кавычках и которые не имеют пробела, меткой с надписью «-9 «Отсутствует»», а затем я хочу зафиксировать каждое из этих отрицательных значений в хэш вместе с именем переменной (например, V10, V11), чтобы я мог распечатать их позже в операторе перекодирования. Я читаю этот файл через Perl, разделяя «строки» на буквальную точку, за которой следует новая строка (что означает конец команды в SPSS). Однако код, который я придумал до сих пор, заменяет и фиксирует только одно совпадение отрицательного значения на «строку», и я не уверен, что делаю неправильно. Мой текущий код выглядит так:

my %negmiss;
my @lines = split(/(\.\n)/,$_);
foreach my $line (@lines) {
    my $modline = $line;
    if ($line =~ /VALUE LABELS\s(\S+)/g) {
       my $label_name = $1;
       if ($line =~ /\n(-\d+(\.\d+)?)\n/g) {
           $modline =~ /\n(-\d+(\.\d+)?)\n/\n -9 \"Missing\"\n/g;
           push my @negname, $label_name;
           push @{$negmiss{$label_name}}, $1;
       }
    }
print $modline;
}
foreach (@negname) {
    print "RECODE $_ (@{ $negmiss{$_} } = -9\.\n";
}

Это частично работает, но опять же, это только подстановка и захват одного отрицательного значения для каждой из «строк», поэтому мой вывод выглядит следующим образом:

VALUE LABELS V10
 -9 "Missing"
 1 "Yes".

VALUE LABELS V11
 -9 "Missing"
-33.33
 -10 "Don't Know".

RECODE V10 (-1 = -9).
RECODE V11 (-50.00 = -9).

Как я могу захватить и заменить как -50,00, так и -33,33 для «линии» V11?

Изменить: я хочу, чтобы мой вывод выглядел так:

VALUE LABELS V10
 -9 "Missing"
 1 "Yes".

VALUE LABELS V11
 -9 "Missing"
 -9 "Missing"
 -10 "Don't Know".

RECODE V10 (-1 = -9).
RECODE V11 (-50.00 = -9).
RECODE V11 (-33.33 = -9).

person CGhost    schedule 13.04.2017    source источник
comment
Как должен выглядеть ваш вывод с учетом этого ввода?   -  person Sobrique    schedule 13.04.2017
comment
Сообщение отредактировано, чтобы показать, как должен выглядеть вывод.   -  person CGhost    schedule 13.04.2017


Ответы (1)


Корень вашей проблемы здесь:

/\n(-\d+(\.\d+)?)\n/\n -9 \"Missing\"\n/g;

Поскольку вы добавляете к шаблону префикс и суффикс \n, это означает, что ваш второй "\n" потребляется предыдущим совпадением, поэтому он не будет совпадать дважды.

Измените это на

/\n(-\d+(\.\d+)?)(?=\n)/\n -9 \"Missing\"\n/g;

(или, возможно, лучше просто $), и все должно быть в порядке.

E.g.:

#!/usr/bin/env perl

use strict;
use warnings;

local $/ = '';

while ( <DATA> ) {
   s/\n(-\d+(\.\d+)?)(?=\n)/\n -9 \"Missing\"/g;
   print;
}

__DATA__
VALUE LABELS V10
-1
 1 "Yes".

VALUE LABELS V11
-50.00
-33.33
 -10 "Don't Know".

Ваша «навязчивая» линия:

       push my @negname, $label_name;

Хотя он мало что делает - вероятно, лучше обойтись без этого. Я бы также посоветовал вам установить разделитель записей, потому что это означает, что вы можете работать запись за записью.

$/, установленный на '', будет работать в «режиме абзаца» — пустая строка разделена.

Может как-то так?:

#!/usr/bin/env perl

use strict;
use warnings;

my %recode; 

local $/ = '';

while ( <DATA> ) {
   my ( $label_name ) = m/VALUE LABELS (\S+)/;
   my @recode = m/^\s*(\-[\d\.]+)$/gm;
   $recode{$label_name} = \@recode;

   s/\n(-\d+(\.\d+)?)(?=\n)/\n -9 \"Missing\"/g;
   print;
}

print "\n\n";

foreach my $key ( sort keys %recode ) {
   foreach my $value ( @{$recode{$key}} ) {
     print "RECODE $key ( $value = -9 )\n";
   }
}

__DATA__
VALUE LABELS V10
-1
 1 "Yes".

VALUE LABELS V11
-50.00
-33.33
 -10 "Don't Know".

Это дает вывод:

VALUE LABELS V10
 -9 "Missing"
 1 "Yes".

VALUE LABELS V11
 -9 "Missing"
 -9 "Missing"
 -10 "Don't Know".

RECODE V10 ( -1 = -9 )
RECODE V11 ( -50.00 = -9 )
RECODE V11 ( -33.33 = -9 )
person Sobrique    schedule 13.04.2017
comment
Ага. Отредактировал его снова, теперь у меня есть желаемый результат. Надеюсь, это поможет? - person Sobrique; 13.04.2017
comment
У меня были некоторые проблемы с этим в течение минуты, так как мне пришлось адаптировать это в соответствии с моим синтаксисом, опубликованным выше (поскольку это небольшая часть более крупного скрипта), что означало сохранение окружающего цикла foreach вместо цикла while. Я немного застрял, но в конце концов я изменил оператор if на оператор foreach, и он отлично сработал. Для справки в будущем, означает ли это, что я не могу сопоставлять/захватывать одну и ту же строку несколько раз в строке в одном и том же операторе if? Что мне нужно использовать foreach или while для операций «множественного числа»? - person CGhost; 13.04.2017