多行匹配和解析

时间:2014-09-30 15:29:48

标签: regex perl multiline

我需要在多行上解析一些数据。我希望在一行上有一些数据点来创建一个类似于结构的表。数据各不相同。有些是在多行上分解的,有些只是2行。

示例数据

情景1:

Start Anchor - First parse boundary
Name1 - Only one line of name
12345 - Number line needed
ending anchor - End parse boundary
data
data
data

情景2:

Start Anchor
name1 - Two lines of name.
name2
12345987
ending anchor
data
data
data

期望的结果

情景1:

Name1  12345
data
data
data

情景2:

name1 name2  12345987
data
data
data

解释

所以在这个数据中,我需要同一行上的名称和相应的数字。我想将\n替换为解析边界之间的内容,同时仍保留其余数据。

我当前的结果


情景1:

name1 12345987 - easy because only 1 line of name
data
data

情景2:

name1 name2 - tough part
12345987
data
data

代码

my $text = read_file( 'list_2.txt' );
use File::Slurp;
$text =~ s/^Start Anchor\n(.*?)\n(.*?)ending anchor/$1 $2/gism;
print $text;
print;

在我看来,$1变量应该捕获名称,因为使用了结束锚。 $2变量应该捕获数字,因为它总是在结束锚点之上一行。

4 个答案:

答案 0 :(得分:1)

正在打败你的是非贪婪的通配符。模式^Start Anchor\n(.*?)\n(.*?)ending anchor将在Start Anchor\n之后与少数字符匹配到下一个换行符,即第一个名称行。然后下一个捕获将匹配 - 尽可能少的字符,但在这里没有区别 - 到ending anchor,whiuch是第二个名称行和数字,以及它们之间的换行符,因为你有有效的/s修饰符。

解析内存中的整个文件很少是一个简单的工作,通常更好的选择是逐行读取并保持状态数据记住你在结构中的位置。

在这种情况下,一个简单的嵌套读循环就可以了。您没有说多个块是否可以出现在同一个文件中,但是此解决方案可以处理我认为您想要的情况。

use strict;
use warnings;

my @name;

while (<DATA>) {
  if (/^Start Anchor/) {
    while (<DATA>) {
      last if /^ending anchor/;
      chomp;
      push @name, $_;
    }
    print "@name\n";
    @name = ()
  }
  else {
    print;
  }
}

__DATA__
Start Anchor
Name1
12345
ending anchor
data
data
data

Start Anchor
name1
name2
12345987
ending anchor
data
data
data

<强>输出

Name1 12345
data
data
data

name1 name2 12345987
data
data
data

答案 1 :(得分:0)

您仍然可以逐行处理文件,只需记住您当前处于的状态或阶段:

#!/usr/bin/perl
use warnings;
use strict;

my $state = 'search anchor';
my @names;
while (<DATA>) {
    if ('search anchor' eq $state and /Start Anchor/) {
        $state = 'collect names';

    } elsif ('collect names' eq $state) {

        chomp;
        push @names, $_;

        $state = 'expect ending anchor' if /^[0-9]+$/;

    } elsif ('expect ending anchor' eq $state) {

        die 'Ending anchor not found' unless /ending anchor/;
        $state = 'data';
        print "@names\n"

    } elsif ('data' eq $state) {

        if (/Start Anchor/) {
            $state = 'collect names';
            @names = ();

        } else {
            print;
        }
    }
}

__DATA__
Start Anchor
Name1
12345
ending anchor
data
data
data

Start Anchor
name1
name2
12345987
ending anchor
data
data
data

答案 2 :(得分:0)

第二个名称是可选的。 正则表达式使用eval修饰符来进行替换格式化 它不是必需的,如果需要,只需替换$1 $2 $3\n就可以完成。

 # /(?xm)^Start\ Anchor\n\s*^(\w.*)\n(?:^(\w.*)\n)?\s*^(\d+).*\n\s*^ending\ anchor(?:\n|$)/

 (?xm-)
 ^ Start\ Anchor \n 
 \s* 
 ^ 
 ( \w .* )                          # (1), Name1 required
 \n 
 (?:
      ^ 
      ( \w .* )                     # (2), Name2 optional
      \n 
 )?
 \s* 
 ^ 
 ( \d+ )                            # (3), Numbers
 .* \n 
 \s* 
 ^ ending\ anchor
 (?: \n | $ )

Perl代码:

use strict;
use warnings;

$/ = undef;

my $data = <DATA>;

$data =~ 
   s/
     ^Start\ Anchor\n\s*^(\w.*)\n(?:^(\w.*)\n)?\s*^(\d+).*\n\s*^ending\ anchor(?:\n|$)
    /
     "$1 ".(defined $2 ? "$2 " : "") . "$3\n"
    /exmg;

print $data, "\n";

__DATA__

Start Anchor
name1
12345
ending anchor
data
data
data


Start Anchor
name1
name2
12345987
ending anchor
data
data
data

输出:

name1 12345
data
data
data


name1 name2 12345987
data
data
data

答案 3 :(得分:0)

两个提示

  1. 逐行处理文件而不是诽谤

  2. Range Operator ..用于跨越多行的逻辑

  3. 以下是根据需要清理您的脚本:

    use strict;
    use warnings;
    use autodie;
    
    my $file = 'list_2.txt';
    
    #open my $fh, '<', $file;
    my $fh = \*DATA;
    
    while (<$fh>) {
        if ( my $range = /^Start Anchor/ .. /^ending anchor/ ) {
            if ( $range =~ /E/ ) {
                print "\n";
            } elsif ( $range > 1 ) {
                chomp;
                print ' ' if $range > 2;
                print;
            }
        } else {
            print;
        }
    }
    
    __DATA__
    Start Anchor
    Name1
    12345
    ending anchor
    data
    data
    data
    
    Start Anchor
    name1
    name2
    12345987
    ending anchor
    data
    data
    data
    

    输出:

    Name1 12345
    data
    data
    data
    
    name1 name2 12345987
    data
    data
    data