【问题标题】:What's the best way to read a huge CSV file using Perl?使用 Perl 读取巨大的 CSV 文件的最佳方法是什么?
【发布时间】:2011-01-11 13:21:59
【问题描述】:

要求

  • 我有一个非常大的 CSV 文件要读取。 (约 3 GB)
  • 我不需要所有记录,我的意思是,我们可以使用一些条件,例如,如果 CSV 的第 3 列内容为“XXXX”,第 4 列为“999”。

问题: 我可以使用这些条件来改进读取过程吗?如果是这样,我该如何使用 Perl 来做到这一点?

我需要一个示例(Perl 脚本)作为您的答案。

【问题讨论】:

  • 请注意,所有答案都使用while 循环而不是for 循环。这个非常重要。 while 在标量上下文中运行,一次只能获取一行。 for 在列表上下文中运行,将导致在任何处理开始之前将整个文件读入内存。
  • -1 表示“我不会告诉你我已经尝试过什么,也不会给出任何迹象表明我自己已经努力做到这一点,但所有答案都应该包括工作代码"

标签: perl file csv


【解决方案1】:

这里有一个解决方案:

#!/usr/bin/env perl
use warnings;
use strict;
use Text::CSV_XS;
use autodie;
my $csv = Text::CSV_XS->new();
open my $FH, "<", "file.txt";
while (<$FH>) {
    $csv->parse($_);
    my @fields = $csv->fields;
    next unless $fields[1] =~ /something I want/;
    # do the stuff to the fields you want here
}

【讨论】:

  • 在解析调用中缺少部分解引用运算符。而且,正则表达式格式不正确,但除了那个很好的例子。
【解决方案2】:

使用Text::CSV

【讨论】:

  • 对于这样一个非常大的文件,您应该使用 Text::CSV_XS
  • 如果系统上存在 ::_XS,它将使用它。
  • 换句话说:XS 模块通常比纯 Perl 模块提供更好的内存和/或 CPU 性能,这对于您描述的大文件很有帮助。见en.wikipedia.org/wiki/XS_%28Perl%29
【解决方案3】:

您的 a) 问题已被多次回答,但 b) 尚未得到解决:

我不需要所有记录,我的意思是, 我们有一些条件 可以使用,例如,如果第 3 个 CSV 列内容有 'XXXX' 和 4th 列有'999'。我可以用这些吗 改善阅读的条件 过程?

没有。如果不先读取该行,您如何知道第 3 CSV 列是否包含“XXXX”或第 4 列是否包含“999”? (DBD::CSV 允许您将其隐藏在 SQL WHILE 子句后面,但是,由于 CSV 是未索引数据,它仍然需要读取每一行以确定哪些匹配条件,哪些不匹配。)

几乎可以使用一行内容让您跳过阅读文件部分的唯一方法是它是否包含告诉您 1)“跳过此行之后的部分”和 2)“继续阅读字节偏移 nnn"。

【讨论】:

    【解决方案4】:

    Text::CSV 模块是一个很好的解决方案。另一种选择是DBD::CSV 模块,它提供了一个稍微不同的界面。如果您正在开发必须访问来自不同形式的数据库(包括关系数据库和逗号分隔的文本文件)的数据的应用程序,那么 DBI 接口非常有用。

    下面是一些示例代码:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use DBI;
    
    $dbh = DBI->connect ("DBI:CSV:f_dir=/home/joe/csvdb") 
        or die "Cannot connect: $DBI::errstr";
    
    $sth = $dbh->prepare ("SELECT id, name FROM info.txt WHERE id > 1 ORDER by id");
    $sth->execute;
    
    my ($id,$name);
    $sth->bind_columns (\$id, \$name);
    while ($sth->fetch) {
        print "Found result row: id = $id, name = $name\n";
    }
    $sth->finish;
    

    除非您打算与其他类型的数据库通信,否则我会使用 Text::CSV 来完成此任务,但在 Perl TIMTOWDI 中,了解您的选择会有所帮助。

    【讨论】:

    • DBD::CSV 不会将整个文件加载到内存中吗?
    【解决方案5】:

    使用像 Text::CSV 这样的模块,但是,如果您知道您的数据不会嵌入逗号及其简单的 CSV 格式,那么一个简单的 while 循环来迭代文件就足够了

    while (<>){
      chomp;
      @s = split /,/;
      if ( $s[2] eq "XXXX" && $s[3] eq "999" ){
        # do something;
      } 
    }
    

    【讨论】:

      猜你喜欢
      • 2017-04-15
      • 2010-09-24
      • 2013-05-27
      • 2010-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-21
      相关资源
      最近更新 更多