2010-11-19 2 views
3

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

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

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

ответ

0

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

11

Поскольку 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 
+3

хороший ответ. Это действительно заманчиво просто запустить 'split /, /', но это не совсем хорошо для CSV-файлов Micro $ oft-style. Я не думаю, что это даже достаточно хорошо для файлов foocap, разделенных двоеточиями. – tchrist

+0

tchrist: True. Отлично, чтобы получить комментарий от вас, сэр! Спасибо. :-) –

+0

Новое на Perl. Это решение в памяти? есть ли простые способы сделать то же самое с большими файлами? спасибо – Gevorg

6

Там есть также 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
-2

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

#!/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 
+2

Отлично, если у вас нет имени «John Doe, Esq.». – reinierpost

+1

Есть причина, почему у нас есть синтаксические модули CSV, такие как Text :: CSV. Простое разделение на запятую недостаточно для общего случая. –

7

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

3

оригинальный плакат попросил не третьей стороной модулей (что я ничего не имею в виду от CPAN). Хотя это ограничение, которое будет ужасно ограничивать вашу способность писать хороший современный код Perl, в этом случае возможно использование (core) Text :: ParseWords модуля вместо (неосновного) текста :: 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 
Смежные вопросы