【问题标题】:How can I filter out specific column from a CSV file in Perl?如何从 Perl 中的 CSV 文件中过滤掉特定列?
【发布时间】:2010-09-30 10:03:11
【问题描述】:

我只是 Perl 的初学者,在使用 Perl 脚本过滤列时需要一些帮助。 我在一个文件中有大约 10 列用逗号分隔,我需要在该文件中保留 5 列并删除该文件中的所有其他列。我们如何实现这一目标?

非常感谢任何人的帮助。

干杯, 尼尔

【问题讨论】:

  • 请提供更多细节,尤其是各列的区别。数据的一个例子会很棒。

标签: perl csv


【解决方案1】:

查看Text::CSV(或Text::CSV_XS)以在 Perl 中解析 CSV 文件。它在CPAN 上可用,或者如果您使用 Linux 或其他类 Unix 操作系统,您也可以通过包管理器获得它。在 Ubuntu 中,该软件包称为 libtext-csv-perl。

它可以处理像被引用的字段这样的情况,因为它们包含逗号,这是简单的拆分命令无法处理的。

【讨论】:

  • 我不确定 Text::CSV_XS 是什么,或者它与 Text::CSV 有何不同,但是当我安装 libtext-csv-perl 时,我显然两者都有。
  • _XS 表示外部子程序,这意味着它是用另一种语言(通常是 C)编写的,并且结果更快。对于 Text::CSV 和 Text_CSV_XS,该模块的作者非常友好地提供了仅 Perl 的实现 (Text::CSV) 和更快的 C 实现 (Text::CSV_XS)。
【解决方案2】:

CSV 是一种定义不明确的复杂格式(引号、逗号和空格的奇怪问题)。寻找a library,它可以为您处理细微差别,并为您提供便利,例如按列名索引。

当然,如果您只是想用逗号分割文本文件,那么@Pax 的解决方案就是您的最佳选择。

【讨论】:

    【解决方案3】:

    使用 split 将行分开,然后输出您想要的行(比如每隔一列),创建以下 xx.pl 文件:

    while(<STDIN>) {
        chomp;
        @fields = split (",",$_);
        print "$fields[1],$fields[3],$fields[5],$fields[7],$fields[9]\n"
    }
    

    然后执行:

    $ echo 1,2,3,4,5,6,7,8,9,10 | perl xx.pl
    2,4,6,8,10
    

    【讨论】:

    • 不要只使用 split 除非您确信这些值不包含任何逗号
    • 我认为“CSV”的定义排除了这一点
    • CSV 的某些变体允许在引号内使用逗号。
    • 我最喜欢这个解决方案,最适合初学者。稍后引入模块。
    【解决方案4】:

    如果您谈论的是 Windows 中的 CSV 文件(例如,从 Excel 生成的文件),您需要小心处理本身包含逗号但用引号括起来的字段。

    在这种情况下,简单的 split 将不起作用。

    【讨论】:

      【解决方案5】:

      或者,您可以使用标准库中的Text::ParseWords。添加

      use Text::ParseWords;
      

      到上面 Pax 示例的顶部,然后替换

        my @fields = parse_line(q{,}, 0, $_);
      

      用于拆分。

      【讨论】:

        【解决方案6】:

        您可以使用 Perl 的一些内置运行时选项在命令行上执行此操作:

        $ echo "1,2,3,4,5" | perl -a -F, -n -e 'print join(q{,}, $F[0], $F[3]).qq{\n}' 1,4

        以上将 -a(utosplit) 使用 -F(ield) 逗号。然后它将加入您感兴趣的字段并将它们打印出来(使用行分隔符)。这假定没有嵌套逗号的简单数据。我使用不可打印的字段分隔符 (\x1d) 执行此操作,所以这对我来说不是问题。

        更多详情请见http://perldoc.perl.org/perlrun.html#Command-Switches

        【讨论】:

          【解决方案7】:

          去寻找并没有找到一个很好的兼容 csv 的过滤器程序,它不仅可以灵活地用于其中一个,因此我写了一个。享受吧。

          基本用法是:

          bash$ csvfilter [-r ]* [-quote]

          #!/usr/bin/perl 使用严格; 使用警告; 使用 Getopt::Long; 使用文本::CSV; 我的 $always_quote=0; 我的@remove; if ( !GetOptions('remove:s'=> \@remove, 'quote-always'=>sub {$always_quote=1;}) ) { die "$0:invalid option (use --remove [--quote-always])"; } 我的@cols2remove; 子过滤器(@) { 我的@fields=@_; 我的@r; 我的 $i=0; 对于我的 $c (@cols2remove) { 我的 $p; #if ( $i $i ) { 推(@r,拼接(@fields,$i)); } 返回@r; } # 如果这些则只创建一个 我的 $csvOut=new Text::CSV({always_quote=>$always_quote}); 子打印线(@) { 我的@fields=@_; 我的 $combined=$csvOut->combine(filter(@fields)); 我的 $str=$csvOut->string(); 如果(长度($str)){ 打印“$str\n”; } } 我的 $csv = Text::CSV->new(); 我的$od; 打开($od,“|猫”)||死“输出:$!”; 尽管 () { $csv->解析($_); 如果 ( $. == 1 ) { 我的 $failures=0; 我的@cols=$csv->字段; 对于我的 $rm (@remove) { for (我的 $c=0; $c$b} @cols2remove); } printLine($csv->fields); } 退出(0); \

          【讨论】:

            【解决方案8】:

            除了这里的人所说的处理逗号分隔文件之外,我想指出,可以使用数组切片和/或映射来提取偶数(或奇数)数组元素:

            @myarray[map { $_ * 2 } (0 .. 4)]
            

            希望对你有帮助。

            【讨论】:

              【解决方案9】:

              我个人最喜欢的 CSV 方法是使用 AnyData module。它似乎使事情变得非常简单,并且可以很容易地删除命名列。 Take a look on CPAN.

              【讨论】:

                【解决方案10】:

                这回答了一个更大的问题,但似乎是一个很好的相关信息。

                unix cut 命令可以做你想做的事(还有更多)。一直是reimplemented in Perl

                【讨论】:

                • 不是真的; cut 无法使用字符串中的逗号等管理 CSV 的奥秘。
                • 那么我可能不会调用那些 CSV 文件。
                • 很多其他的东西都称它们为 CSV 文件,并且在存在好的解决方案的情况下,我们不需要求助于不太好的解决方案:)
                猜你喜欢
                • 2017-10-04
                • 1970-01-01
                • 2021-07-22
                • 1970-01-01
                • 2016-09-09
                • 2023-04-08
                • 1970-01-01
                • 2019-09-20
                • 1970-01-01
                相关资源
                最近更新 更多