Perl - 正则表达式操纵.csv

时间:2014-12-04 12:50:41

标签: regex perl csv

我在Perl中有一个函数读取文件夹中最后修改过的.csv,并将其值解析为变量。

我发现正则表达式存在一些问题。 我的.csv看起来像:

Title is: "NAME_NAME_NAME"
"Period end","Duration","Sample","Corner","Line","PDP OUT TOTAL","PDP OUT OK","PDP OUT NOK","PDP OUT OK Rate"
"04/12/2014 11:00:00","3600","1","GPRS_OUT","ARG - NAME 1","536","536","0","100%"
"04/12/2014 11:00:00","3600","1","GPRS_OUT","USA - NAME 2","1850","1438","412","77.72%"
"04/12/2014 11:00:00","3600","1","GPRS_OUT","AUS - NAME 3","8","6","2","75%"


.(ignore this dot, you will understand later)

到目前为止,我已经有了一些帮助,可以通过以下方法将值解析为一些变量:

open my $file, "<", $newest_file
        or die qq(Cannot open file "$newest_file" for reading.);
while ( my $line = <$file> ) {

    my ($date_time, $duration, $sample, $corner, $country_name, $pdp_in_total, $pdp_in_ok, $pdp_in_not_ok, $pdp_in_ok_rate) 
            = parse_line ',', 0, $line;

    my ($date, $time) = split /\s+/, $date_time;
    my ($country, $name) = $country_name =~ m/(.+) - (.*)/;

    print "$date, $time, $country, $name, $pdp_in_total, $pdp_in_ok_rate";
}

问题是:

  1. 我不知道如何使第一行和第二行(即.csv中的列名)被忽略;
  2. 该文件有时在文件末尾有2-5个空行,正如我在我的示例中所示(忽略其末尾的点,它在文件中不存在)。
  3. 我该怎么做?

4 个答案:

答案 0 :(得分:3)

当你有一个带有列标题的csv文件并希望将数据解析为变量时,最简单的选择是使用Text::CSV。此代码显示了如何将数据导入哈希引用$row。 (即my %data = %$row

use strict;
use warnings;
use Text::CSV;
use feature 'say';

my $csv = Text::CSV->new({
        binary  => 1,
        eol => $/,
    });
# open the file, I use the DATA internal file handle here
my $title = <DATA>;

# Set the headers using the header line
$csv->column_names( $csv->getline(*DATA) );

while (my $row = $csv->getline_hr(*DATA)) {
    # you can now access the variables via their header names, e.g.:
    if (defined $row->{Duration}) {  # this will skip the blank lines
        say $row->{Duration};
    }
}

__DATA__
Title is: "NAME_NAME_NAME"    
"Period end","Duration","Sample","Corner","Line","PDP IN TOTAL","PDP IN OK","PDP IN NOT OK","PDP IN OK Rate"
"04/12/2014 10:00:00","3600","1","GRPS_INB","CHN - Name 1","1198","1195","3","99.74%"
"04/12/2014 10:00:00","3600","1","GRPS_INB","ARG - Name 2","1198","1069","129","89.23%"
"04/12/2014 10:00:00","3600","1","GRPS_INB","NLD - Name 3","813","798","15","98.15%"

如果我们使用$row打印其中一个Data::Dumper变量,则会显示我们从Text::CSV返回的结构:

$VAR1 = {
          'PDP IN TOTAL' => '1198',
          'PDP IN NOT OK' => '3',
          'PDP IN OK' => '1195',
          'Period end' => '04/12/2014 10:00:00',
          'Line' => 'CHN - Name 1',
          'Duration' => '3600',
          'Sample' => '1',
          'PDP IN OK Rate' => '99.74%',
          'Corner' => 'GRPS_INB'
        };

答案 1 :(得分:1)

  

1)我不知道如何使第一行(即.csv中的列名)被忽略;

while ( my $line = <$file> ) {
    chomp $line;
    next  if $. == 1 || $. == 2;
  

2)文件有时在文件末尾有2-5个空行,正如我在我的示例中所示(忽略其末尾的点,它在文件中不存在)。

while ( my $line = <$file> ) {
    chomp $line;
    next if $. == 1 || $. == 2;
    next if $line =~ /^\s*$/;

答案 2 :(得分:1)

open ...
my $names_from_first_line = <$file>; # you can use them or just ignore them

while($my line = <$file>) {
    unless ($line =~ /\S/) {
        # skip empty lines
        next;
    }
..
}

另外,请考虑使用Text :: CSV来处理CSV格式

答案 3 :(得分:1)

您知道有效行将以日期开头。我建议您只是跳过不以您期望的格式开头的行:

while ( my $line = <$file> ) {
    warn qq(next if not $line =~ /^"\d{2}-\d{2}-d{4}/;);  # Temp debugging line
    next if not $line =~ /^"\d{2}-\d{2}-d{4}/;
    warn qq($line matched regular expression);            # Temp debugging line
    ...
}

/^"\d{2}-\d{2}-d{4}",/regular expression pattern。模式位于/.../

之间
  • ^ - 开始行。
  • " - 引号。
  • \d{2} - 后跟两位数。
  • - - 后面有一个破折号。
  • \d{2] - 后跟两位数。
  • - - 后面有一个破折号。
  • \d{4} - 后跟四位数字

这应该描述您的行的第一部分,即MM-DD-YYYY格式的日期,用引号括起来,后面跟一个逗号。 =~告诉Perl您希望左侧的东西与右侧的正则表达式匹配。

正则表达式可能难以理解,这也是为什么Perl具有只写语言的声誉的原因之一。正则表达式被比作 sailor cussing 。但是,正则表达式是一个非常强大的工具,值得学习。有了一些经验,您就可以轻松解码它们。

next if...语法类似于:

if (...) {
    next;
}

通常情况下,您不应该使用后期修复if,也不要使用unlessif相反)。它们会使您的程序更难理解。但是,当像这样放在一个循环的开头行之后,他们会清楚地表明你过滤掉了你不想要的行。我本来可以这样写的(很多人会认为这是可取的):

 next unless $line =~ /^"\d{2}-\d{2}-d{4}",/;

这就是说你想跳过行,除非它们与你的正则表达式匹配。这完全取决于个人偏好,你认为对于明年来到这里的穷人俱乐部来说更容易,并且必须弄清楚你的计划在做什么。

我实际上考虑过这个并决定if not ...说我希望文件中的几乎所有行都符合我的格式,我想抛弃少数例外。对我而言,next unless ...表示有一些行符合我的正则表达式,而且有很多行不符合,我想只处理匹配的行。< / p>

让我们进入编程的下一部分:注意会破坏程序的事情。我之前的回答并没有做很多错误检查,但它应该。如果一条线与您的格式不匹配会怎样?如果split没有用,该怎么办?如果田地不是我所期望的怎么办?您应该检查每个语句以确保它确实有效。如果它们不起作用,Perl中的几乎所有函数都将返回零,空字符串或undef。例如,open语句。

open my $file, "<", $newest_file
    or die qq(Cannot open file "$newest_file" for reading.);

如果open不起作用,则返回文件句柄值为零。 or表示如果open没有返回非零文件句柄,请执行后续行以杀死您的程序。

所以,仔细查看你的程序,看看你假设某些东西按预期运行的地方,并想一想如果它没有发生会发生什么。然后,如果您获得该异常,请在程序中添加检查。可能是您要报告错误或记录错误并跳到下一行。可能是你希望你的程序戛然而止。可能是您可以从错误中恢复并继续。无论你做什么,检查可能的错误(特别是来自用户输入)并处理可能的错误。


调试

我告诉你正则表达式很棘手。是的,假设你的日期是一个单独的领域,我犯了一个错误。相反,它后跟一个空格然后是时间,这意味着正则表达式中的最终",不应该存在。我已修复上述代码。但是,您可能仍需要测试和调整。这让我们在Perl中进行调试。

您可以使用warn语句来帮助调试您的程序。如果您复制一个语句,然后用warn qq(...);包围它,Perl将打印出该行(填写变量)和行号。我甚至在各种编辑器中创建宏来为我做这个。

qq(...)quote like operator。这是在字符串周围做双引号的另一种方法。好处是字符串可以包含实际的引号,qq(...);仍然有效。

完成调试后,您可以搜索warn语句并将其删除。 Perl带有强大的built in debugger,许多IDE都集成了它。但是,有时候只需要在一些warn语句中轻松查看代码中发生了什么 - 特别是如果您遇到正则表达式问题。

相关问题