几周前,我发布了一个关于我正在解析不规则格式的数据文件的问题。以下是数据样本:
01-021412 15/02/2007 207,000.00 14,839.00 18 -6 2 6 6 5 16 6 4 4 3 -28 -59 -88 -119
-149 -191 -215 -246
Atraso Promedio ---> 2.88
我需要一个程序,它将提取01-021412,18,计算并汇总后续系列中的所有数字,并存储atraso promedio,这可能会重复此操作超过40,000个。我收到了一个非常有帮助的response,从中可以编写代码:
use strict;
use warnings;
#Create an output file
open(OUT, ">outFull.csv");
print OUT "loanID,nPayments,atrasoPromedio,atrasoAlt,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72\n";
open(MYINPUTFILE, "<DATOS HISTORICO ASPIRE2.txt");
my @payments;
my $numberOfPayments;
my $loanNumber;
while(<MYINPUTFILE>)
{
if(/\b\d{2}-\d{6}\b/)
{
($loanNumber, undef, undef, undef, $numberOfPayments, @payments) = split;
}
elsif(m/---> *(\d*.\d*)/)
{
my (undef, undef, undef, $atrasoPromedio) = split;
my $N = scalar @payments;
print "$numberOfPayments,$N,$loanNumber\n";
if($N==$numberOfPayments){
my $total = 0;
($total+=$_) for @payments;
my $atrasoAlt = $total/$N;
print OUT "$loanNumber,$numberOfPayments,$atrasoPromedio,$atrasoAlt,",join( ',', @payments),"\n";
}
}
else
{
push(@payments, split);
}
}
这样可以正常工作,除了大约50%的条目包含如下的'*'这一事实:
* 01-051948 06/03/2009 424,350.00 17,315.00 48 0 6 -2 0 21 10 9 13 10 9 7 13 3 4
12 -3 14 8 6
Atraso Promedio ---> 3.02
星号会导致程序失败,因为它会中断拆分模式,从而导致不正确的变量分配。到目前为止,我已经通过从输入数据文件中删除星号来解决这个问题,但我刚刚意识到,通过这样做,程序实际上完全省略了这些贷款。是否有一种经济的方法来修改我的脚本,以便它处理有和没有星号的条目?
顺便说一句,如果一个条目确实包含一个星号,我想在输出数据中记录这个事实。
非常感谢, 亚伦
答案 0 :(得分:1)
使用中间数组:
my $has_asterisk;
# ...
if(/\b\d{2}-\d{6}\b/)
{
my @fields = split;
$has_asterisk = $fields[0] eq '*';
shift @fields if $has_asterisk;
($loanNumber, undef, undef, undef, $numberOfPayments, @payments) = @fields;
}
答案 1 :(得分:1)
您可以在进行拆分之前丢弃星号:
while(<MYINPUTFILE>) {
s/^\s*\*\s*//;
if(/\b\d{2}-\d{6}\b/) {
($loanNumber, undef, undef, undef, $numberOfPayments, @payments) = split;
...
除此之外,你应该使用3个args open,lexical filehandles并测试打开失败。
my $file = 'DATOS HISTORICO ASPIRE2.txt';
open my $MYINPUTFILE, '<', $file or die "unable to open '$file' for reading : $!";
答案 2 :(得分:0)
所以看起来你的第一个if语句正则表达式没有考虑'*',那么我们如何修改它。我的perl正则表达式技巧有点生疏,请注意这是未经测试的。
if(/(?:\* )?\b\d{2}-\d{6}\b/)
*
是一个修饰符,表示“零次或多次”,因此我们需要将其转义,\*
(?: )
表示“将它们组合在一起但不保存”,我只是使用它,以便我可以同时将?
应用于空格和*
答案 3 :(得分:0)
在while
循环开始时,试试这个:
...
while(<MYINPUTFILE>)
{
my $asterisk_exists = 0;
if (s/^\* //) {
$asterisk_exists = 1;
}
...
除了使用s///
功能删除星号外,您还可以首先跟踪星号是否在那里。删除星号后,脚本的其余部分应该正常运行。