Сортировать CSV на основе определенного столбца? - PullRequest
4 голосов
/ 19 ноября 2010

Я уверен, что я делал это в прошлом, и я забываю кое-что маленькое, но как мне отсортировать файл CSV по определенному столбцу? Мне интересны ответы с и без сторонних модулей Perl. В основном методы без, так как у меня не всегда есть доступ для установки дополнительных модулей.

Пример данных:

name,25,female
name,24,male
name,27,female
name,21,male

желаемый конечный результат после сортировки по 2-му числовому столбцу:

name,21,male
name,24,male
name,25,female
name,27,female

Ответы [ 6 ]

12 голосов
/ 19 ноября 2010

Поскольку CSV - довольно сложный формат, лучше использовать модуль, который делает за нас работу .

Ниже приведен пример использования модуля Text :: CSV :

#!/usr/bin/env perl

use strict;
use warnings;

use constant AGE => 1;

use Text::CSV;

my $csv = Text::CSV->new();

my @rows;
while ( my $row_ref = $csv->getline( \*DATA ) ) {
    push @rows, $row_ref;
}

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

for my $row_ref (@rows) {
    $csv->combine(@$row_ref);
    print $csv->string(), "\n";
}

__DATA__
name,25,female
name,24,male
name,27,female
name,21,male
8 голосов
/ 19 ноября 2010

В духе того, что всегда есть другой способ сделать это, имейте в виду, что простой старой сортировки 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)

Если вам нужно решение Perl, поместите его в qx (); -)

6 голосов
/ 19 ноября 2010

Существует также DBD :: CSV :

#!/usr/bin/perl

use strict; use warnings;
use DBI;

my $dbh = DBI->connect('dbi:CSV:', undef, undef, {
    RaiseError => 1,
    f_ext => '.csv',
    csv_tables => { test => { col_names => [qw' name age sex '] } },
});

my $sth = $dbh->prepare(q{
    SELECT name, age, sex FROM test ORDER BY age
});

$sth->execute;

while ( my @row = $sth->fetchrow_array ) {
    print join(',' => @row), "\n";
}

$sth->finish;
$dbh->disconnect;

Выход:

name,21,male
name,24,male
name,25,female
name,27,female
3 голосов
/ 19 ноября 2010

В оригинальном постере не было никаких сторонних модулей (что, как я понимаю, ничего не значит для CPAN). Хотя это ограничение будет ужасно ограничивать вашу способность писать хороший современный код на Perl, в этом случае можно использовать (основной) модуль Text :: ParseWords вместо (неосновного) Text :: CSV. Итак, в значительной степени заимствуя пример Алана, мы получим:

#!/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
0 голосов
/ 19 ноября 2010

Я бы сделал что-то вроде этого:

#!/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
0 голосов
/ 19 ноября 2010

Когда вы предоставляете свой собственный код сравнения, вы можете сортировать что угодно.Просто извлеките нужный элемент с помощью регулярного выражения, или, возможно, в этом случае разделите, а затем сравните с этим.Если у вас много элементов, я бы проанализировал данные в списке списков, а затем код сравнения мог бы получить к ним доступ без разбора.Это исключит повторный анализ одной и той же строки по сравнению с другими строками.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...