在具有正向lookbehind的多行数据中解析字符串

时间:2017-11-17 16:03:48

标签: perl parsing multiline

我正在尝试解析数据:

header1
-------
var1 0
var2 5
var3 9
var6 1

header2
-------
var1 -3
var3 5
var5 0

现在我想得到var2 for header2。这是最好的方法吗?

到目前为止,我是通过

逐行解析我的文件
open(FILE,"< $file");
while (my $line = <FILE>){
    # do stuff
}

但我想它无法正确处理多行解析。

现在我正在考虑立即解析文件但到目前为止还没有成功......

my @Input;
open(FILE,"< $file");
while (<FILE>){ @Input = <FILE>; }
if (@Input =~ /header2/){ 
    #...
}

2 个答案:

答案 0 :(得分:3)

处理此问题的更简单方法是“段落模式”。

local $/ = "";
while (<>) {
    my ($header, $body) =~ /^([^\n]*)\n-+\n(.*)/s
       or die("Bad data");

    my @data = map [ split ], split /\n/, $body;

    # ... Do something with $header and @data ...
}

同样可以在不弄乱$/的情况下实现,如下所示:

my @buf;
while (1) {
    my $line = <>;
    $line =~ s/\s+\z// if !defined($line);
    if (!length($line)) {
       if (@buf) {
          my $header = shift(@buf);
          shift(@buf);
          my @data = map [ split ], splice(@buf);

          # ... Do something with $header and @data ...
       }

       last if !defined($line);
       next;
    }

    push @buf, $line;
}

(事实上,第二个片段包含了第一个小改进。)

快速评论您的尝试:

  • while循环无用,因为@Input = <FILE>会将文件的其余行放在@Input中。
  • @Input =~ /header2/header2与数组的字符串化进行匹配,这是@Input中元素数量的字符串化。如果您想检查@Input包含header2的元素,是否需要循环遍历@Inputs的元素并单独检查它们。

答案 1 :(得分:2)

while (<FILE>){ @Input = <FILE>; }

这没有多大意义。 “虽然您可以从FILE读取记录,但请将FILE上的所有数据读入@Input”。我想你真正想要的只是:

my @Input = <FILE>;
if (@Input =~ /header2/){ 

这也很奇怪。绑定运算符(=~)期望标量操作数,因此它在标量上下文中计算两个操作数。这意味着@Input将被评估为@Input中的元素数量。这是一个整数,永远不会匹配“header2”。

有两种方法。首先是正则表达式。

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

my $file = 'file';

open my $fh, '<', $file or die $!;

my $data = join '', <$fh>;

if ($data =~ /header2.+var3 (.+?)\n/s) {
  say $1;
} else {
  say 'Not found';
}

关键是/s运算符上的m//。没有它,正则表达式中的两个点将不匹配换行符。

另一种方法更多的是逐行解析器。

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

my $file = 'file';

open my $fh, '<', $file or die $!;

my $section = '';

while (<$fh>) {
  chomp;
  # if the line all word characters,
  # then we've got a section header.
  if ($_ !~ /\W/) {
    $section = $_;
    next;
  }

  my ($key, $val) = split;
  if ($section eq 'header2' and $key eq 'var3') {
    say $val;
    last;
  }
}

我们一次读取一行文件,并记下章节标题。对于数据行,我们在空格上进行拆分并检查我们是否在正确的部分并使用正确的密钥。

在这两种情况下,我都转而使用更标准的方法(词法文件句柄,3-arg open()or die $!)来打开文件。