使用Perl将CSV文件从特定行解析到文件末尾

时间:2012-08-31 11:54:50

标签: perl csv perl-module

对Perl来说很新,需要你的帮助

我有一个CSV文件xyz.csv,内容为:

这里的level1和er值是字符串名称......不是数字......

level1,er
level2,er2
level3,er3
level4,er4

我使用下面的脚本解析此CSV文件,并在第一次运行中将字段传递给数组

open(my $d, '<', $file) or die "Could not open '$file' $!\n";
while (my $line = <$d>) {
  chomp $line; 
  my @data = split "," , $line; 
  @XYX = ( [ "$data[0]", "$data[1]" ], );
}

对于第二次运行,我从命令提示符处获取输入并存储在变量$val中。我的程序应该从存储在变量中的值解析CSV文件,直到它到达文件的末尾

例如

我输入level2所以我需要一个脚本从第二行解析到CSV文件的末尾,忽略文件中level2之前的值,然后传递这些值({{1 } level2}到level4

@XYX = (["$data[1]","$data[1]"],);}

我输入level2,er2 level3,er3 level4,er4 所以我需要一个脚本从第三行解析到CSV文件的末尾,忽略文件中level3之前的值,然后传递这些值({{1 }和level3)到level3

level4

我如何实现这一目标?请提出宝贵的建议。感谢您的帮助

4 个答案:

答案 0 :(得分:4)

只要您确定数据中有从不任何逗号,您就可以使用split。但即便如此,将分割限制为两个字段也是明智之举,这样你就可以获得第一个逗号及其后的所有内容

您的代码存在一些问题。首先,我希望您将use strictuse warnings放在所有Perl程序的顶部。这个简单的措施可以解决许多你可以忽略的微不足道的问题,所以在你寻求代码帮助之前这一点尤为重要

通常不知道,但在"\n"字符串末尾添加换行符die会阻止Perl在错误发生位置的输出中提供文件和行号详细信息。虽然这可能是你想要的,但给予额外信息通常会更有帮助

您的变量名称确实无用,并且按照惯例,Perl变量由小写字母数字和下划线组成。像@XYX$W这样的名称根本无法帮助我理解您的代码!

而不是拆分为数组,看起来你最好将两个字段放入两个标量变量中以避免所有索引。我不确定你的意图@XYX = (["$data[1]","$data[1]"],)。首先,你真的想要两次使用$data[1]吗?其次,你应该从不将标量变量放在双引号中,因为它做了非常具体的事情,除非你知道那是什么,否则你应该避免它。最后,你是说每次围绕循环push一个匿名数组到@XYX吗?否则,每次从文件中读取一行时都会覆盖数组的内容,并且先前的数据将丢失

此程序使用正则表达式从第一个字段中提取$level_num。它只是找到字符串中的第一个数字序列,然后可以将其与最小所需级别$min_level进行比较,以确定日志中的一行是否相关

use strict;
use warnings;

my $file = 'xyz.csv';
my $min_level = 3;
my @list;

open my $fh, '<', $file or die "Could not open '$file' $!";

while (my $line = <$fh>) {
  chomp $line; 
  my ($level, $error) = split ',', $line, 2;
  my ($level_num) = $level =~ /(\d+)/;
  next unless $level_num >= $min_level;
  push @list, [ $level, $error ];
}

答案 1 :(得分:1)

为了决定要处理哪些记录,你可以沿着这些行使用“触发器”操作符(..)。

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

my $level = shift || 'level1';

while (<DATA>) {
  if (/^\Q$level,/ .. 0) {
    print;
  }
}

__DATA__
level1,er
level2,er2
level3,er3
level4,er4

触发器运算符返回false,直到其第一个操作数为真。此时它返回false,直到第二个操作数为真;此时它再次返回false。

我假设你的文件是订购的,所以一旦你开始处理它,你永远不想停止。这意味着触发器的第一个操作数可以是/^\Q$level,/(匹配行开头的字符串$level),第二个操作数可以只为零(因为我们从不希望它停止处理)。

我还强烈建议使用split /,/解析CSV记录。这可能适用于您当前的数据,但通常情况下,CSV文件中的字段允许包含嵌入式逗号,这将破坏此方法。相反,请查看Text::CSVText::ParseWords(包含在标准Perl发行版中)。

更新:我似乎对此有一些支持。如果人们花时间解释原因,那就太好了。

答案 2 :(得分:1)

#!/usr/bin/perl

use strict;
use warnings;
use Text::CSV;

my @XYZ;
my $file = 'xyz.csv';
open my $fh, '<', $file or die "$file: $!\n";

my $level = shift; # get level from commandline
my $getall = not defined $level; # true if level not given on commandline

my $parser = Text::CSV->new({ binary => 1 }); # object for parsing lines of CSV

while (my $row = $parser->getline($fh)) # $row is an array reference containing cells from a line of CSV
{
  if ($getall # if level was not given on commandline, then put all rows into @XYZ
      or      # if level *was* given on commandline, then...
      $row->[0] eq $level .. 0 # ...wait until the first cell in a row equals $level, then put that row and all subsequent rows into @XYZ
     )
  {
    push @XYZ, $row;
  }
}

close $fh;

答案 3 :(得分:0)

#!/usr/bin/perl  
use strict;     
use warnings;
open(my $data, '<', $file) or die "Could not open '$file' $!\n"; 
my $level = shift ||"level1"; 
while (my $line = <$data>) {  
chomp $line; 
my @fields = split "," , $line; 
if($fields[0] eq $level .. 0){
print "\n$fields[0]\n";
print "$fields[1]\n";
}}

这很有效....感谢所有人的帮助......