如何在Perl中的匹配括号之间提取字符串?
题
我的输入文件如下:
HEADER
{ABC|*|DEF {GHI 0 1 0} {{Points {}}}}
{ABC|*|DEF {GHI 0 2 0} {{Points {}}}}
{ABC|*|XYZ:abc:def {GHI 0 22 0} {{Points {{F1 1.1} {F2 1.2} {F3 1.3} {F4 1.4}}}}}
{ABC|*|XYZ:ghi:jkl {JKL 0 372 0} {{Points {}}}}
{ABC|*|XYZ:mno:pqr {GHI 0 34 0} {{Points {}}}}
{
ABC|*|XYZ:abc:pqr {GHI 0 68 0}
{{Points {{F1 11.11} {F2 12.10} {F3 14.11} {F4 16.23}}}}
}
TRAILER
我想将文件提取到下面的数组中:
$array[0] = "{ABC|*|DEF {GHI 0 1 0} {{Points {}}}}"
$array[1] = "{ABC|*|DEF {GHI 0 2 0} {{Points {}}}}"
$array[2] = "{ABC|*|XYZ:abc:def {GHI 0 22 0} {{Points {{F1 1.1} {F2 1.2} {F3 1.3} {F4 1.4}}}}}"
..
..
$array[5] = "{
ABC|*|XYZ:abc:pqr {GHI 0 68 0}
{{Points {{F1 11.11} {F2 12.10} {F3 14.11} {F4 16.23}}}}
}"
这意味着,我需要将第一个打开的卷发括号与其闭合的卷发支撑匹配,然后在之间提取字符串。
我已经检查了以下链接,但这不适用于我的问题。将卷曲括号之间的绳子串起来“ {我想要卷曲括号之间的东西}”
我正在尝试,但如果有人可以帮助我获得他们的专业知识,这确实会有所帮助...
谢谢Sri ...
解决方案
至少在现代版本的perl中,这肯定可以用正则表达式来完成:
my @array = $str =~ /( \{ (?: [^{}]* | (?0) )* \} )/xg;
print join "\n" => @array;
REGEX匹配一个卷曲支架块,该块包含非卷发支架字符或递归中的递归(匹配嵌套的括号)
编辑:上面的代码在Perl 5.10+中起作用,对于早期版本,递归更详细:
my $re; $re = qr/ \{ (?: [^{}]* | (??{$re}) )* \} /x;
my @array = $str =~ /$re/xg;
其他提示
采用 文字::平衡
我第二YSTH的建议使用 Text::Balanced
模块。几行将使您前进。
use strict;
use warnings;
use Text::Balanced qw/extract_multiple extract_bracketed/;
my $file;
open my $fileHandle, '<', 'file.txt';
{
local $/ = undef; # or use File::Slurp
$file = <$fileHandle>;
}
close $fileHandle;
my @array = extract_multiple(
$file,
[ sub{extract_bracketed($_[0], '{}')},],
undef,
1
);
print $_,"\n" foreach @array;
输出
{ABC|*|DEF {GHI 0 1 0} {{Points {}}}}
{ABC|*|DEF {GHI 0 2 0} {{Points {}}}}
{ABC|*|XYZ:abc:def {GHI 0 22 0} {{Points {{F1 1.1} {F2 1.2} {F3 1.3} {F4 1.4}}}}}
{ABC|*|XYZ:ghi:jkl {JKL 0 372 0} {{Points {}}}}
{ABC|*|XYZ:mno:pqr {GHI 0 34 0} {{Points {}}}}
{
ABC|*|XYZ:abc:pqr {GHI 0 68 0}
{{Points {{F1 11.11} {F2 12.10} {F3 14.11} {F4 16.23}}}}
}
您总是可以计算牙套:
my $depth = 0;
my $out = "";
my @list=();
foreach my $fr (split(/([{}])/,$data)) {
$out .= $fr;
if($fr eq '{') {
$depth ++;
}
elsif($fr eq '}') {
$depth --;
if($depth ==0) {
$out =~ s/^.*?({.*}).*$/$1/s; # trim
push @list, $out;
$out = "";
}
}
}
print join("\n==================\n",@list);
这是旧的,普通的perl风格(可能是丑陋的)。
我认为您在这里要使用的纯正式表达式不是您想要使用的(恕我直言,这甚至可能无法使用Regex可简化)。
相反,建立一个小解析器,类似于此处所示: http://www.perlmonks.org/?node_id=308039(请参阅2003年11月18日18:29 UTC的Shotgunefx(Parson)的答案)
更新 看来可能与正则表达式可行 - 我在 掌握正则表达式 (这可以在Google书籍上找到,因此,如果您没有这本书,可以搜索谷歌搜索 - 请参阅第5章,“匹配平衡的括号集”部分)
您使用状态机的方法要比对这种类型的解析要好得多。
正则表达式实际上对于匹配牙套来说是非常不好的。根据您想走的深度,您可以编写一个完整的语法(这比听起来容易得多!) 解析::回收. 。或者,如果您只想获取块,请搜索打开“ {'mark and Closing'}'的打开,只要在任何给定时间保持多少打开。