Perl 根据某一列对CSV进行排序?

Perl 根据某一列对CSV进行排序?,perl,sorting,Perl,Sorting,我肯定我以前做过,但有一点我忘记了,但是我怎么能对某个列上的CSV文件进行排序呢?我对有无第三方Perl模块的答案感兴趣。主要是没有方法,因为我不总是有权安装额外的模块 示例数据: name,25,female name,24,male name,27,female name,21,male 姓名,25岁,女 姓名,24岁,男 姓名,27岁,女 姓名,21岁,男 在第二个数字列上排序后所需的最终结果: name,21,male name,24,male name,25,female name,2

我肯定我以前做过,但有一点我忘记了,但是我怎么能对某个列上的CSV文件进行排序呢?我对有无第三方Perl模块的答案感兴趣。主要是没有方法,因为我不总是有权安装额外的模块

示例数据:

name,25,female name,24,male name,27,female name,21,male 姓名,25岁,女 姓名,24岁,男 姓名,27岁,女 姓名,21岁,男 在第二个数字列上排序后所需的最终结果:

name,21,male name,24,male name,25,female name,27,female 姓名,21岁,男 姓名,24岁,男 姓名,25岁,女
name,27,女性当您提供自己的比较代码时,您可以对任何内容进行排序。只需使用正则表达式提取所需的元素,或者在本例中可能使用拆分,然后进行比较。如果您有很多元素,我会将数据解析为列表列表,然后比较代码可以访问它而无需解析。与其他行相比,这将消除对同一行的反复解析。

由于CSV是一种非常复杂的格式,最好使用一个模块为我们完成这项工作

以下是使用该模块的示例:

#/usr/bin/env perl
严格使用;
使用警告;
使用常数年龄=>1;
使用Text::CSV;
my$csv=Text::csv->new();
我的@行;
而(my$row\u ref=$csv->getline(\*DATA)){
按@行,$row\u ref;
}
@rows=sort{$a->[AGE]$b->[AGE]}@rows;
对于我的$row\u ref(@rows){
$csv->combine(@$row\u ref);
打印$csv->string(),“\n”;
}
__资料__
姓名,25岁,女
姓名,24岁,男
姓名,27岁,女
姓名,21岁,男
还有:

输出:

name,21,male name,24,male name,25,female name,27,female 姓名,21岁,男 姓名,24岁,男 姓名,25岁,女
姓名,27岁,女性我会这样做:

#!/usr/bin/perl
use warnings;
use strict;

my @rows = map { chomp; [split /[,\s]+/, $_] } <DATA>; #read each row into an array
my @sorted = sort { $a->[1] <=> $b->[1] } @rows; # sort the rows (numerically) by second column

for (@sorted) {
  print join(', ', @$_) . "\n"; # print them out as CSV
}

__DATA__
name,25,female
name,24,male
name,27,female
name,21,male
#/usr/bin/perl
使用警告;
严格使用;
my@rows=map{chomp;[split/[,\s]+/,$\u]}#将每一行读入一个数组
my@sorted=sort{$a->[1]$b->[1]}@rows;#按第二列对行(数字)排序
for(@sorted){
打印联接(“,”,@$#)。“\n”#将它们打印为CSV
}
__资料__
姓名,25岁,女
姓名,24岁,男
姓名,27岁,女
姓名,21岁,男

本着总是有另一种方法可以做到这一点的精神,请记住,简单的GNU排序可能就足够了

$ sort -t, -k2 -n unsorted.txt
name,21,male
name,24,male
name,25,female
name,27,female
其中,命令行参数为:

-t, # use comma as the record separator
-k2 # sort on the second key (record) in the line
-n  # sort using numerical comparison (like using <=> instead of cmp in perl)
-t,#使用逗号作为记录分隔符
-k2#按行中的第二个键(记录)排序
-n#使用数值比较进行排序(如在perl中使用而不是cmp)

如果您想要Perl解决方案,请将其包装在qx()中;-)

最初的海报没有要求第三方模块(我认为CPAN没有任何意义)。虽然这一限制将严重限制您编写优秀的现代Perl代码的能力,但在本例中,可以使用(核心)Text::ParseWords模块代替(非核心)Text::CSV。因此,借用Alan的例子,我们得到:

#!/usr/bin/env perl

use strict;
use warnings;

use Text::ParseWords;

my @rows;

while (<DATA>) {
    push @rows, [ parse_line(',', 0, $_) ];
}

@rows = sort { $a->[1] <=> $b->[1] } @rows;

foreach (@rows) {
    print join ',', @$_;
}

__DATA__
name,25,female
name,24,male
name,27,female
name,21,male
#/usr/bin/env perl
严格使用;
使用警告;
使用Text::ParseWords;
我的@行;
而(){
按@行[parse_line(',',0,$)];
}
@rows=sort{$a->[1]$b->[1]}@rows;
foreach(@行){
打印联接“,”,@$;
}
__资料__
姓名,25岁,女
姓名,24岁,男
姓名,27岁,女
姓名,21岁,男
使用Raku(née Perl6)

这是一个相当快速且肮脏的解决方案,主要用于“手动”CSV。只要每行只有一(1)个年龄,代码就可以工作:读取行
$a
,用逗号包围1到3个
,并分配给
@b
,导出排序索引
$c
,使用
$c
对行进行重新排序
$a

~$ raku -e 'my $a=lines();  my @b=$a.comb(/ \, <(\d**1..3)> \, /).pairs;  my $c=@b.sort(*.values)>>.keys.flat;  $a[$c.flat]>>.put;' sort_age.txt
name,21,male
name,24,male
name,25,female
name,27,female
要反向排序,请将
.reverse
添加到创建
$c
的方法链的末尾。再次更改
else
占位符参数,将缺少有效年龄的行移到顶部或底部。此外,可以使用三元运算符编写上面的
@b
的创建:
my@b=do for@a{(m/\,\,/)??+$/!!999},作为替代方案

以下是供后代使用的未排序输入文件:

$ cat sort_age.txt
name,,male
name,"",female
name,9999,male
name,NA,male
name,25,female
name,24,male
name,27,female
name,21,male


回答得好。只运行
split/,/
,确实很有诱惑力,但对于Micro$oft样式的CSV文件来说,这还远远不够。我认为它对于Unix冒号分隔的foocap文件来说还不够好。很高兴收到您的评论,先生!谢谢。:-)Perl新手。这是内存解决方案吗?有没有简单的方法来处理大文件?感谢您,只要您的名字中没有
John Doe,Esq.
。我们有像Text::CSV这样的CSV解析模块是有原因的。在一般情况下,仅仅用逗号分隔是不够的。
~$ raku -e 'my $a=lines();  my @b=$a.comb(/ \, <(\d**1..3)> \, /).pairs;  my $c=@b.sort(*.values)>>.keys.flat;  $a[$c.flat]>>.put;' sort_age.txt
name,21,male
name,24,male
name,25,female
name,27,female
~$ raku -e 'my @a=lines(); my @b = do for @a {if $_ ~~ m/ \, <(\d**1..3)> \, / -> { +$/ } else { 999 }; }; my $c=@b.pairs.sort(*.values)>>.keys.flat;  @a[$c.flat]>>.put;' sort_age.txt
name,21,male
name,24,male
name,25,female
name,27,female
name,,male
name,"",female
name,9999,male
name,NA,male
$ cat sort_age.txt
name,,male
name,"",female
name,9999,male
name,NA,male
name,25,female
name,24,male
name,27,female
name,21,male