我在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";
}
问题是:
我该怎么做?
答案 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
,也不要使用unless
(if
相反)。它们会使您的程序更难理解。但是,当像这样放在一个循环的开头行之后,他们会清楚地表明你过滤掉了你不想要的行。我本来可以这样写的(很多人会认为这是可取的):
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
语句中轻松查看代码中发生了什么 - 特别是如果您遇到正则表达式问题。