【问题标题】:Perl - Capture and Substitute Multiple Matches in an If StatementPerl - 在 If 语句中捕获和替换多个匹配项
【发布时间】:2017-04-13 14:35:16
【问题描述】:

我一直在构建一个脚本,以从 IBM 的 SPSS Statistics 包中获取一些输出并将其转换为 SPSS 输入语法。我目前遇到以下问题,我似乎无法弄清楚。我有一些如下所示的文本:

VALUE LABELS V10
-1
 1 "Yes".

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

我想用标有“-9 "Missing"' 的标签替换引号中没有跟标签并且没有空格缩进的负值,然后我想捕获其中的每一个散列中的负值以及变量名称(即 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).

如何捕获和替换 V11“行”的 -50.00 和 -33.33?

编辑:我希望我的输出如下所示:

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).

【问题讨论】:

  • 鉴于输入,您的输出应该是什么样的?
  • 已编辑帖子以显示输出应该是什么样子。

标签: regex perl pcre regex-group


【解决方案1】:

问题的根源在这里:

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

因为您在 前加上 \n 后缀您的模式,这意味着您的 second "\n" 已被前一个匹配消耗 - 所以它不会匹配两次.

改成

/\n(-\d+(\.\d+)?)(?=\n)/\n -9 \"Missing\"\n/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 )

【讨论】:

  • 是的。再次编辑它,现在我得到了你想要的输出。希望这能解决问题吗?
  • 我在这方面遇到了一些麻烦,因为我不得不调整它以满足我上面发布的语法(因为这是一个较大脚本的一小部分),这意味着保持周围的 foreach 循环而不是一个while循环。我被卡住了一会儿,但最终我将 if 语句更改为 foreach 语句,并且效果很好。以供将来参考,这是否意味着我不能在同一 if 语句中的同一行中多次匹配/捕获同一字符串?我需要使用 foreach 或 while 进行“复数”操作吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-03
  • 2022-07-05
  • 1970-01-01
  • 2015-02-05
  • 2019-12-02
  • 2022-01-08
相关资源
最近更新 更多