perl text :: csv - 过滤csv文档中的特定列并丢弃其他列

时间:2010-10-26 17:36:13

标签: perl

我想用正则表达式过滤掉特定的列并丢弃其他列。例如,如果我有以下列名:

日期 mem_total cpu.usagemhz.average_0 cpu.usagemhz.average_1 cpu.usagemhz.average_2

我想只捕获以“cpu.usage.mhz.average”开头的列

text :: csv的特定功能是否可以帮助我快速检查列名?

谢谢! JD

* 更新 * *

我尝试了jimtut的答案,它非常接近我正在寻找的东西。再次感谢大家!

以下是来自jimtut的代码,底部的print语句有一个小编辑。我添加了print $ colCount只是为了看看数据是怎么回事;

use Text::CSV;

my $file = "foo.csv";
my $pattern = ".*In";
open(F, $file) or warn "Warning! Unable to open $file\n";

my $lineCount = 0;
my %desiredColumns;
while(<F>) {
  $lineCount++;
  my $csv = Text::CSV->new();
  my $status = $csv->parse($_); # should really check this!
  my @fields = $csv->fields();
  my $colCount = 0;

  if ($lineCount == 1) {
    # Let's look at the column headings.
    foreach my $field (@fields) {
      $colCount++;
      if ($field =~ m/$pattern/) {
        # This heading matches, save the column #.
        $desiredColumns{$colCount} = 1;
      }
    }
  }
  else {
    # Not the header row.  Parse the body of the file.
    foreach my $field (@fields) {
      $colCount++;
      if (exists $desiredColumns{$colCount}) {
        # This is one of the desired columns.
        # Do whatever you want to do with this column!
        print "$colCount\t$field\n";
      }
    }
  }
}
close(F);

以下是结果

colCount |  $field

12      565
13      73
14      36
15      32
16      127
17      40
18      32
19      42
20      171
12      464
13      62
14      32
15      24
16      109
17      21
18      19
19      39
20      150
12      515
13      76
14      28
15      30
16      119
17      15
18      25
19      46
20      169
12      500
13      71
14      30
15      28
16      111
17      20
18      18
19      40
20      167

我想将此数据添加到单个数组或哈希值。你怎么看?类似......

foreach专栏{ 检查是否已存在包含该列号的哈希。如果没有,则创建哈希。 }

然后遍历每个字段并将字段数据添加到适当的哈希值。

你认为这是解决这个问题的正确方法吗?

3 个答案:

答案 0 :(得分:2)

不,不是Text :: CSV中的特定功能。我会做这样的事情:

use Text::CSV;

my $file = "foo.csv";
my $pattern = "cpu.usage.mhz.average.*";
open(F, $file) or die "Unable to open $file: $!\n";

my $lineCount = 0;
my %desiredColumns;
my %columnContents;

while(<F>) {
  $lineCount++;
  my $csv = Text::CSV->new();
  my $status = $csv->parse($_); # should really check this!
  my @fields = $csv->fields();
  my $colCount = 0;

  if ($lineCount == 1) {
    # Let's look at the column headings.
    foreach my $field (@fields) {
      $colCount++;
      if ($field =~ m/$pattern/) {
        # This heading matches, save the column #.
        $desiredColumns{$colCount} = 1;
      }
    }
  }
  else {
    # Not the header row.  Parse the body of the file.
    foreach my $field (@fields) {
      $colCount++;
      if (exists $desiredColumns{$colCount}) {
        # This is one of the desired columns.
        # Do whatever you want to do with this column!
        push(@{$columnContents{$colCount}}, $field);
      }
    }
  }
}
close(F);

foreach my $key (sort keys %columnContents) {
  print "Column $key: " . join(",", @{$columnContents{$key}}) . "\n\n";
}

希望有所帮助!我确信有人可以用Perl单行写入,但这对我来说更容易阅读......

答案 1 :(得分:1)

为什么你想要这样做吗?是否最小化存储?消除解析许多不需要的列的处理成本?

如果是后者,则无法避免那种处理成本。您提出的任何解决方案都将STILL读取并解析100%的文件。

如果是前者,有很多方法,有些方法比其他方法更有效。

另外,你究竟是什么意思“帮我快速检查一下列名?”?如果您想获取列名称,可以使用column_names()方法,前提是您使用column_names(getline($fh))设置了列名称。


如果您只想在散列中返回特定的列名,以便在不需要的列上浪费内存,那么就没有明确的API。您可以自己动手,或滥用getline_hr()方法的“错误/功能”:

  • 对于前者(自己动手),您可以执行以下操作:

    my $headers = $csv->getline( $fh ); # First line is headers.
    my @headers_keep = map { /^cpu.usage.mhz.average/ ? 1 : 0 } @$headers;
    while ( my $row = $csv->getline( $fh ) ) {
        my $i = 0;
        my @row_new = grep { $headers_keep[$i++] } $@row;
        push @rows, \@row_new;
    }
    

    但你可以自己滚动。

  • 您还可以使用“getline_hr()”的“功能”,如果列名重复,则不会将值分配给哈希值(仅分配LAST版本)\

    在您的情况下,对于列名:date,mem_total,cpu.usagemhz.average_0,cpu.usagemhz.average_1,cpu.usagemhz.average_2,只需将column_names数组设置为在数组的前2个eements中包含“cpu.usagemhz.average_0”值 - 它们将不会被{{保存1}}。

    您可以查看列列表,找到“不需要”列的连续范围,并将其名称替换为该范围后面的第一个所需列的名称。唯一的分岔点是,如果“不需要”的范围位于列的最后 - 用“ JUNK ”替代。

答案 2 :(得分:1)

由于您感兴趣的字段位于索引2-4,我们只需从getline()返回的字段数组中取出它们。这个示例代码打印出来但你可以随心所欲地做任何事情。

use Text::CSV;                                     # load the module
my $csv = Text::CSV->new ();                       # instantiate
open $fh, "<somefile";                             # open the input
while ( my $fields = $csv->getline($fh) ) {        # read a line, and parse it into fields
    print "I got @{$fields}[2..4]\n";              # print the fields of interest
}
close ($fh)                                        # close when done