2014-09-30 69 views
0

我有一些数据需要解析,它在多行上。我想在某一行上创建一个类似于结构的表格。数据各不相同。有些在一条以上的线路上分解,有些仅仅是两条线路。多行匹配和解析

示例数据

方案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应该捕获该数字,因为它始终位于结束锚点的上方一行。

回答

1

这是被你打败了非贪婪通配符。图案^Start Anchor\n(.*?)\n(.*?)ending anchor将匹配几个个字符尽可能在Start Anchor\n之后到下一个换行符,即第一个命名行。然后下一个捕获将匹配 - 再次尽可能少的字符,但在这里没有什么区别 - 对于ending anchor,这是第二个名字行和数字,以及它们之间的换行符,因为您有/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 
+0

因此,嵌套循环说明是否有一个起始锚点,它有一个结束锚点chomp或删除它们之间的所有内容的新行。然后打印整行并返回,否则按原样打印。 @Borodin – JDE876 2014-09-30 18:30:14

+0

@ JDE876:是的。只需复制行,直到找到起始锚;读取线条并将它们推到'@ name'上,直到结束锚点转向;在一行中打印“@ name”的内容,将其清空,然后继续复制。 – Borodin 2014-09-30 18:37:03

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 
+0

' undef @ names'强制将数组的内存分配释放,这可能不是你想要的。一个简单的'@names =()'将清空数组,但保留分配的空间,并且是通常的首选项。 – Borodin 2014-09-30 16:09:58

+0

@Borodin:是的。更新。 – choroba 2014-09-30 16:14:11

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 
0

两个尖端

  1. 过程文件逐行而不是啜

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

以下是清理你的脚本,你的愿望的作品的:

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