2011-02-04 55 views
1

先谢谢您放纵一个业余的Perl问题。我从一个大的,未格式化的文本文件中提取一些数据,并且无法将'while'循环和正则表达式匹配用于多行。使用'while'循环评估多行代码时出现问题,Perl

首先,数据的一个示例:

01-034575 18/12/2007 258,750.00 11,559.00 36  -2  0  6 -3  2 -2  0  2  1 -1  3  0  5 15 
                 -13 -44 -74 -104 -134 -165 -196 -226 -257 -287 -318 -349 -377 -408 -438 
                 -469 -510 -541 -572 -602 -633 -663 
     Atraso Promedio --->  0.94 

第一序列,XX-XXXXXX是贷款ID号。日期和以下两个数字并不重要。 '36'是付款次数。以下顺序的正数和负数表示该客户在36个付款期间中的每一个的这一贷款多晚/早。 “Atraso Promedio”之后的'0.94'是银行的平均延迟计算。问题在于它是错误的,因为它们会将系列中的所有负面(即早期)支付替换为零,从而有效地说明客户的风险程度。我需要编写一个提取ID和支付次数的程序,然后动态计算多行平均延迟。

这是我到目前为止有:

#Create an output file 
open(OUT, ">out.csv"); 
print OUT "Loan_ID,Atraso_promedio,Atraso_alt,N_payments,\n"; 

open(MYINPUTFILE, "<DATA.txt"); 
while(<MYINPUTFILE>){ 

    chomp($_); 

    if($ID_select != 1 && m/(\d{2}\-\d{6})/){$Loan_ID = $1, $ID_select = 1} 

    if($ID_select == 1 && m/\d{1,2},\d{1,3}\.00\s+\d{1,2},\d{1,3}\.00\s+(\d{1,2})/) {$N_payments = $1, $Payment_find = 1}; 

    if($Payment_find == 1 && $ID_select == 1){ 

      while(m/\s{2,}(\-?\d{1,3})/g){ 
       $N++; 
       $SUM = $SUM + $1; 
       print OUT "$Loan_ID,$1\n"; #THIS SHOWS ME WHAT NUMBERS THE CODE IS GRABBING. ACTUAL OUTPUT WILL BE WRITTEN BELOW 
       print $Loan_ID,"\n"; 
      } 


     if(m/---> *(\d*.\d*)/){$Atraso = $1, $Atraso_select = 1} 
     if($ID_select == 1 && $Payment_find == 1 && $Atraso_select == 1){ 
       ... 

还有更精彩的,但while循环是程序被打破。问题在于模式修饰符'g',它执行字符串的全局搜索。这使得程序可以获取我不想要的数字,例如贷款ID中的'1'和支付数量中的'36'。我需要while循环从代码中上一行开始的任何位置开始,在确定了贷款次数后应该是正确的。我已经尝试了所有能够查看的模式修饰符,只有'g'让我摆脱了无限循环。我需要while循环来到行的末尾,然后从下一行开始,而不必梳理已经通过程序馈送的字符串部分。

想法?这有意义吗?将非常感谢您提供的任何帮助。这项工作是无偿的,无偿的:只是试图帮助微型贷款机构的一些朋友进行风险分析。

干杯,
亚伦

回答

2

的问题是采用分体式可能更容易,比如像这样:

use strict; 
use warnings; 

open DATA, "<DATA.txt" or die "$!"; 

my @payments; 
my $numberOfPayments; 
my $loanNumber; 

while(<DATA>) 
{ 
    if(/\b\d{2}-\d{6}\b/) 
    { 
     ($loanNumber, undef, undef, undef, $numberOfPayments, @payments) = split; 
    } 
    elsif(/Atraso Promedio/) 
    { 
     my (undef, undef, undef, $atrasoPromedio) = split; 

     # Calculate average of payments and print results 

    } 
    else 
    { 
     push(@payments, split); 
    } 
} 
+0

很好的建议,非常感谢!斯普利特正是我所需要的。 – Aaron 2011-02-05 02:06:26

0

如果数据的不够干净,我可能会用分裂,而不是常规接近它表达式。如果字段[0]匹配贷款编号的形式并且字段[1]匹配日期的格式,则第一行是可识别的;那么支付日期是字段[5 ..- 1]的数组切片。同样,测试每行的第一个字段可以告诉您数据在哪里。

0

Peter van her Heijden的答案是解决方案的一个很好的简化。

要回答OP关于如何让正则表达式继续停止的问题,请参阅Perl operators - regexp-quote-like operators,特别是紧随其后的部分“在列表上下文中匹配”和“\ G断言”部分。

从本质上讲,您可以使用m//gc以及\G断言来使用正则表达式匹配,其中以前的匹配中断。

关于类似lex的扫描仪的“\ G assertion”部分中的示例似乎适用于此问题。