麻烦使用'while'循环来评估多行,Perl

时间:2011-02-04 07:31:23

标签: perl

提前感谢您放纵一个业余的Perl问题。我正在从一个大的,未格式化的文本文件中提取一些数据,并且在使用'while'循环和在多行上使用正则表达式匹配时遇到了麻烦。

首先,数据样本:

 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循环来到行的末尾,然后从下一个循环开始,而不是梳理已经通过程序提供的字符串部分。

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

干杯,
亚伦

3 个答案:

答案 0 :(得分:2)

使用split可能更容易解决问题,例如:

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);
    }
}

答案 1 :(得分:0)

如果数据足够干净,我可能会使用split而不是正则表达式来处理它。如果field [0]与贷款号码的形式匹配并且字段[1]与日期的格式匹配,则第一行是可识别的;然后付款日期是字段[5 ..- 1]的数组切片。类似地,测试每行的第一个字段可以告诉您数据的位置。

答案 2 :(得分:0)

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

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

基本上,您可以使用m//gc\G断言来使用前一个匹配项中的regexps匹配。

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