我正在尝试设计Perl regex来解析来自IBM的runmqsc实用程序的命令输出。
每个感兴趣的输出行包含一个或多个属性/值对,格式为:“ATTRIBUTE(VALUE)”。属性的值可以为空,也可以包含括号本身。通常,给定行上最多出现两个属性/值对,因此正则表达式是在此假设下编写的。
Perl RE的示例输入:
CHANNEL(TO.IPTWX01) CHLTYPE(CLUSRCVR)
DISCINT(6000) SHORTRTY(10)
TRPTYPE(TCP) DESCR( )
LONGTMR(1200) SCYEXIT( )
CONNAME(NODE(1414)) MREXIT( )
MREXIT( ) CONNAME2(SOME(1416))
TPNAME( ) BATCHSZ(50)
MCANAME( ) MODENAME( )
ALTTIME(00.41.56) SSLPEER()
CONTRIVED() ATTR (00-41-56)
CONTRIVED() DOCTORED()
MSGEXIT( )
我有以下Perl代码来捕获每个属性/值对。
my $resplit = qr/\s+([^\s]+(?:\([^)]*\))?)\s?/;
while ( <IN2> )
{ s/[\s\r\n]+$//;
if ( m/^\s(?:$resplit)(?:$resplit)?$/ )
{ my ($one,$two) = ($1,$2);
print "one: $one, two: $two\n";
}
}
以上代码应用于样本输入时的输出:
one: CHANNEL(TO.IPTWX01), two: CHLTYPE(CLUSRCVR) one: DISCINT(6000), two: SHORTRTY(10) one: TRPTYPE(TCP), two: DESCR( ) one: LONGTMR(1200), two: SCYEXIT( ) one: CONNAME(NODE(1414)), two: MREXIT( ) one: MREXIT( ), two: CONNAME2(SOME(1416)) one: TPNAME( ), two: BATCHSZ(50) one: MCANAME( ), two: MODENAME( ) one: ALTTIME(00.41.56), two: SSLPEER() one: CONTRIVED(), two: ATTR(00-41-56) one: CONTRIVED(), two: DOCTORED() one: MSGEXIT(, two: )
除了输出中的最后一行之外,这很有用 以上。我真的很难弄清楚如何 修改上面的表达式$ resplit来捕获最后一种情况。
任何人都可以提供有关如何完成这项工作的任何想法/建议 另一种方法?
答案 0 :(得分:5)
Text::Balanced模块旨在解决此类问题。这种方法也可以处理任意数量的列。
use strict;
use warnings;
use Text::Balanced qw(extract_bracketed);
my ($extracted, $remainder, $prefix);
while ( defined($remainder = <DATA>) ){
while ( Get_paren_text() ){
$prefix =~ s/ //g;
print $prefix, $extracted, "\n";
}
}
sub Get_paren_text {
($extracted, $remainder, $prefix)
= extract_bracketed($remainder, '()', '[\w ]+');
return defined $extracted;
}
__DATA__
CHANNEL(TO.IPTWX01) CHLTYPE(CLUSRCVR) FOO( ( BAR) )
DISCINT(6000) SHORTRTY(10) BIZZ((((BUZZ) ) ) ) )
TRPTYPE(TCP) DESCR( )
LONGTMR(1200) SCYEXIT( )
CONNAME(NODE(1414)) MREXIT( )
MREXIT( ) CONNAME2(SOME(1416))
TPNAME( ) BATCHSZ(50)
MCANAME( ) MODENAME( )
ALTTIME(00.41.56) SSLPEER()
CONTRIVED() ATTR (00-41-56)
CONTRIVED() DOCTORED()
MSGEXIT( )
答案 1 :(得分:3)
我想尝试使用Regexp::Grammars
。
所以这是:
#! /opt/perl/bin/perl
use strict;
#use warnings;
use 5.10.1;
use Regexp::Grammars;
my $grammar = qr{
<line>
<token: line>
(?: <[pair]> \s* )+
(?{
my $arr = $MATCH{pair};
local $MATCH = {};
for my $pair( @$arr ){
my($key) = keys %$pair;
my($value) = values %$pair;
$MATCH->{$key} = $value;
}
})
<token: pair>
<attrib> \s* \( \s* <value> \s* \)
(?{
$MATCH = {
$MATCH{attrib} => $MATCH{value}
};
})
<token: attrib>
[^()]*?
<token: value>
(?:
<MATCH=pair> |
[^()]*?
)
}x;
use warnings;
my %attr;
while( my $line = <> ){
$line =~ /$grammar/;
for my $key ( keys %{ $/{line} } ){
$attr{$key} = $/{line}{$key};
}
}
use YAML;
say Dump \%attr;
--- ALTTIME: 00.41.56 ATTR: 00-41-56 BATCHSZ: 50 CHANNEL: TO.IPTWX01 CHLTYPE: CLUSRCVR CONNAME: NODE: 1414 CONNAME2: SOME: 1416 CONTRIVED: '' DESCR: '' DISCINT: 6000 DOCTORED: '' LONGTMR: 1200 MCANAME: '' MODENAME: '' MREXIT: '' MSGEXIT: '' SCYEXIT: '' SHORTRTY: 10 SSLPEER: '' TPNAME: '' TRPTYPE: TCP
答案 2 :(得分:1)
while ( <IN2> ) {
while ( /([A-Z]+)\s*(\((?:[^()]*+|(?2))*\))/g ) {
print "$1$2\n";
}
}
这适用于嵌套的parens,例如
CONNAME(NODE(1414, SOME(1416) ) ) ATTR (00-41-56)
(?2)部分是递归的,* +表示“不回溯” - 仅适用于Perl 5.10或更高版本;我是从http://faq.perl.org/perlfaq6.html#Can_I_use_Perl_regul
得到的答案 3 :(得分:0)
#!/usr/bin/perl
use strict;
use warnings;
my @parsed;
while ( my $line = <DATA> ) {
while ( $line =~ / ([A-Z0-9]+) \s* \( (.*?) \) \s /gx ) {
push @parsed, { $1 => $2 }
}
}
use Data::Dumper;
print Dumper \@parsed;
__DATA__
CHANNEL(TO.IPTWX01) CHLTYPE(CLUSRCVR)
DISCINT(6000) SHORTRTY(10)
TRPTYPE(TCP) DESCR( )
LONGTMR(1200) SCYEXIT( )
CONNAME(NODE(1414)) MREXIT( )
MREXIT( ) CONNAME2(SOME(1416))
TPNAME( ) BATCHSZ(50)
MCANAME( ) MODENAME( )
ALTTIME(00.41.56) SSLPEER()
CONTRIVED() ATTR (00-41-56)
CONTRIVED() DOCTORED()
MSGEXIT( )