我有一个看起来像这样的文件:
*NEWRECORD
RECTYPE = D
MH = Calcimycin
AQ = AA
MED = *62
*NEWRECORD
RECTYPE = D
MH = Urinary Bladder
AQ = AB AH BS CH CY DE EM EN GD IM IN IR ME MI PA PH PP PS RA RE RI SE SU TR UL US VI
CX = consider also terms at CYST- and VESIC-
MED = *1359
每个记录块具有不同的行数(例如,CX
条目并不总是存在)。
但如果CX
存在,则仅显示为1个条目。
我们希望得到一个Hash,它将“MH”作为键,将“CX”作为值。
因此解析上述数据我们希望得到这样的结构:
$VAR = { "Urinary Bladder" => ["CYST-" , "VESIC-"]};
解析它的正确方法是什么?
我坚持这个,这不会给我我想要的结果。
use Data::Dumper;
my %bighash;
my $key = "";
my $cx = "";
while (<>) {
chomp;
if (/^MH = (\w+/)) {
$key = $1;
push @{$bighash{$key}}, " ";
}
elsif ( /^CX = (\w+/)) {
$cx = $1;
}
else {
push @{$bighash{$key}}, $cx;
}
}
答案 0 :(得分:5)
如果您使用$/
一次读取一个段落的数据,这会变得更简单。我很惊讶没有其他人建议这样做。
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Data::Dumper;
my %bighash;
$/ = '';
while (<DATA>) {
if (my ($k) = /^MH = (.*?)$/m and my ($v) = /^CX = (.*?)$/m) {
$bighash{$k} = [ $v =~ /([A-Z]+-)/g ];
}
}
say Dumper \%bighash;
__DATA__
*NEWRECORD
RECTYPE = D
MH = Calcimycin
AQ = AA
MED = *62
*NEWRECORD
RECTYPE = D
MH = Urinary Bladder
AQ = AB AH BS CH CY DE EM EN GD IM IN IR ME MI PA PH PP PS RA RE RI SE SU TR UL US VI
CX = consider also terms at CYST- and VESIC-
MED = *1359
输出如下:
$VAR1 = {
'Urinary Bladder' => [
'CYST-',
'VESIC-'
]
};
答案 1 :(得分:3)
尝试以下方法。检查变化(或听Aki)可能是个好主意:
use strict;
use warnings;
use Data::Dumper;
my %bighash;
my $current_key;
while ( <DATA> ) {
chomp;
if ( m/^MH = (.+)/ ) {
$current_key = $1;
} elsif ( /^CX = (.+)/ ) {
my $text = $1;
$bighash{ $current_key } = [ $text =~ /([A-Z]+-)/g ];
}
}
print Dumper ( \%bighash );
__DATA__
*NEWRECORD
RECTYPE = D
MH = Calcimycin
AQ = AA
MED = *62
*NEWRECORD
RECTYPE = D
MH = Urinary Bladder
AQ = AB AH BS CH CY DE EM EN GD IM IN IR ME MI PA PH PP PS RA RE RI SE SU TR UL US VI
CX = consider also terms at CYST- and VESIC-
MED = *1359
更新:使用Regex-Captures代替split
和grep
答案 2 :(得分:1)
最近没有练过我的perl功夫,但最后一句话看起来很可疑。
尝试删除最后一个else语句并在第二个elsif之后直接添加'push'语句。基本上在匹配CX后直接进行推动操作。
另外,您知道MH必须始终出现在CX之前,否则逻辑会中断。
答案 3 :(得分:1)
/^MH = (\w+/)
应为/^MH (\w+)/
。您可能希望使用\s+
或\s*
代替空格if
块else
阻止elsif
块中使用密钥$ key use strict;
和use warnings;
添加到您的代码尝试这些,如果您遇到困难,我会帮助您解决代码
答案 4 :(得分:1)
使用Config::Tiny或Config::YAML对文件进行初始传递然后单独循环遍历每个记录可能更简单。虽然如果你的文件像一个千兆字节或更多,这可能会占用你所有的记忆。
答案 5 :(得分:1)
这是我很快就做的事情,我希望它能给你一个开始的想法:
use Data::Dumper;
# Set your record separator
{
local $/="*NEWRECORD\n";
while(<DATA>) {
# Get rid of your separator
chomp($_);
print "Parsing record # $.\n";
push @records, $_ if ( $_ );
}
}
foreach (@records) {
# Get your sub records
@lines = split(/\n/,$_);
my %h = ();
my %result = ();
# Create a hash from your sub records
foreach (@lines) {
($k, $v) = split(/\s*=\s*/, $_);
$h{$k} = $v;
}
# Parse the CX and strip the lower case comments
$h{ 'CX' } =~ s/[a-z]//g;
$h{ 'CX' } =~ s/^\s+//g;
# Have the upper case values as an array ref in the result hash
$result{ $h{ 'MH' } } = [ split( /\s+/, $h{ 'CX' } ) ] if ( $h{ 'CX' } );
print Dumper( \%h );
print "Result:\n";
print Dumper( \%result );
}
__DATA__
*NEWRECORD
RECTYPE = D
MH = Calcimycin
AQ = AA
MED = *62
*NEWRECORD
RECTYPE = D
MH = Urinary Bladder
AQ = AB AH BS CH CY DE EM EN GD IM IN IR ME MI PA PH PP PS RA RE RI SE SU TR UL US VI
CX = consider also terms at CYST- and VESIC-
MED = *1359