需要帮助使用Perl修复此正则表达式代码?

时间:2012-05-17 22:07:11

标签: regex string perl

我需要您的专业帮助才能使用perl修复此正则表达式代码?

我有这个数据文件......

__Data__
SCSI - test-A
ccccccccccccccccc
aaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbb

__Data__
SCSI - test-B
ccccccccccccccccc
aaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbb

__Data__
SCSI - test-C
ccccccccccccccccc
aaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbb

我想要以下输出

__Data__
SCSI - test-A

__Data__
SCSI - test-B

__Data__
SCSI - test-C

相反,我得到的输出缺少两个数据记录的__Data__

__Data__
SCSI - test-A
SCSI - test-B
SCSI - test-C

这里的代码..

$/ = "__Data__"; # setting the input separator variable to __Data__

while(<ReadFile>)
{
   $_ =~ s/(SCSI.*test-(A|B|C)?)(.*)/$1/ms;
   print $_;
}

4 个答案:

答案 0 :(得分:2)

你告诉Perl这些行以__DATA__结束,所以你得到了

1: "__Data__"
2: "\nSCSI - test-A\nccc\naaa\nbbb\n\n__Data__"
3: "\nSCSI - test-B\nccc\naaa\nbbb\n\n__Data__"
4: "\nSCSI - test-C\nccc\naaa\nbbb\n"

但你错误地认为你得到了

1: "__Data__\nSCSI - test-A\nccc\naaa\nbbb\n\n"
2: "__Data__\nSCSI - test-B\nccc\naaa\nbbb\n\n"
3: "__Data__\nSCSI - test-C\nccc\naaa\nbbb\n"

解决方案:

my $after_data = 0;
while (<>) {
   if (/^__Data__$/) {
      print;
      $after_data = 1;
   }
   elsif ($after_data) {
      print;
      print "\n";
      $after_data = 0;
   }
}

您还可以使用段落模式:

local $/ = '';
while (<>) {
   print /^(.*\n.*\n)/;
   print "\n";
}

答案 1 :(得分:0)

尝试添加

    $\ = $/;

...也设置输出记录分隔符。

但是,你最终会以__Data__这样的最终虚假实例结束,因为它在每条记录之后打印(在每个print的末尾)。

或者,您可以自己拆分输入:

  while (<ReadFile>)
  {   chomp;
      next unless $_ eq '__Data__'; print;
      my $next = <ReadFile>;
      $next =~ s/(SCSI.*text-(A|B|C)?).*/$1/ms;
      print $next;
  }

答案 2 :(得分:0)

将输入记录分隔符设置为空字符串以启用段落模式。在打印中添加换行符。

$/ = ""; # paragraph mode

while (<ReadFile>) {
    $_ =~ s/(SCSI.*test-(A|B|C))(.*)/$1/s;
    print "$_\n\n";
}

答案 3 :(得分:0)

您似乎想要打印适合三个类别之一的行。

  1. __Data__标记
  2. SCSI测试线
  3. 空行
  4. Perl的段落模式在它工作时很方便,但它很脆弱。段落由完全序列"\n\n"终止,但是当段落后面有一个空白但非空行时,不显示空格的编辑器会使调试变得棘手。

    如您的问题中所述,下面的代码会生成您想要的输出。

    #! /usr/bin/env perl
    
    use strict;
    use warnings;
    use 5.10.0;  # smart matching
    
    *ARGV = *DATA;  # for demo only
    
    my @interesting_line = (qr/^__Data__/, qr/SCSI - test-/, qr/^\s*$/);
    
    while (<>) {
      print if $_ ~~ @interesting_line;
      print "\n" if eof && !eof();
    }
    
    __DATA__
    __Data__
    SCSI - test-A
    ccccccccccccccccc
    aaaaaaaaaaaaaaaaa
    bbbbbbbbbbbbbbbbb
    
    __Data__
    SCSI - test-B
    ccccccccccccccccc
    aaaaaaaaaaaaaaaaa
    bbbbbbbbbbbbbbbbb
    
    __Data__
    SCSI - test-C
    ccccccccccccccccc
    aaaaaaaaaaaaaaaaa
    bbbbbbbbbbbbbbbbb
    

    在实际使用中,您将删除标记为仅演示的行,然后在命令行上提供一个或多个数据文件。看起来很滑稽的if eof && !eof()测试试图确定何时在记录之间插入额外的分隔符。如果你想要它完全正确,你需要更加慎重。

    以下是多个文件的输入示例。

    $ cat input1
    __Data__
    SCSI - test-A
    ccccccccccccccccc
    aaaaaaaaaaaaaaaaa
    bbbbbbbbbbbbbbbbb
    
    __Data__
    SCSI - test-B
    ccccccccccccccccc
    aaaaaaaaaaaaaaaaa
    bbbbbbbbbbbbbbbbb
    
    $ cat input2
    __Data__
    SCSI - test-C
    ccccccccccccccccc
    aaaaaaaaaaaaaaaaa
    bbbbbbbbbbbbbbbbb
    
    $ ./extract-tests input1 input2
    __Data__
    SCSI - test-A
    
    __Data__
    SCSI - test-B
    
    __Data__
    SCSI - test-C