2010-11-19 16 views
3

Ich bin mir sicher, dass ich das in der Vergangenheit gemacht habe und es gibt etwas Kleines, das ich vergesse, aber wie kann ich eine CSV-Datei für eine bestimmte Spalte sortieren? Ich bin an Antworten mit und ohne Perl-Module von Drittanbietern interessiert. Hauptsächlich Methoden ohne, da ich nicht immer Zugriff auf zusätzliche Module habe.Sortieren CSV basierend auf einer bestimmten Spalte?

Beispieldaten:

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

gewünschte Endergebnis nach dem Einschalten der zweiten numerischen Spalte Sortierung:

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

Antwort

0

Wenn Sie Ihre eigenen Vergleichscode zur Verfügung stellen, können Sie sich auf etwas sortieren. Extrahieren Sie einfach das gewünschte Element mit einem Regex, oder in diesem Fall wahrscheinlich einem Split, und vergleichen Sie es dann. Wenn Sie viele Elemente haben, würde ich die Daten in eine Liste von Listen analysieren und dann kann der Vergleichscode ohne Parsing darauf zugreifen. Das würde das Parsen der gleichen Zeile im Vergleich zu anderen Zeilen überflüssig machen.

11

Da CSV ein ziemlich komplexes Format ist, ist es besser, ein Modul zu verwenden, das die Arbeit für uns erledigt.

Es folgt ein Beispiel mit dem Text::CSV Modul:

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

nette Antwort. Es ist sehr verlockend, nur ein 'split /, /' auszuführen, aber das ist überhaupt nicht gut genug für die CSV-Dateien im Micro-Dollar-Format. Ich denke nicht, dass es sogar gut genug für Unix Doppelpunkt-getrennte Foocap-Dateien ist. – tchrist

+0

tchrist: Wahr. Schön, einen Kommentar von Ihnen zu erhalten, Sir! Vielen Dank. :-) –

+0

Neu bei Perl. Ist das eine In-Memory-Lösung? Gibt es einfache Möglichkeiten, dasselbe mit großen Dateien zu machen? danke – Gevorg

6

Es gibt auch 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; 

Ausgang:

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

ich so etwas tun würde:

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

Gut, solange du keinen 'John Doe, Esq.' unter deinen Namen hast. – reinierpost

+1

Es gibt einen Grund, warum wir CSV-Parsing-Module wie Text :: CSV haben. Nur ein Komma zu teilen ist im allgemeinen Fall nicht genug. –

7

Im Geiste, dass es immer einen anderen Weg gibt, es zu tun, bedenken Sie, dass die einfache alte GNU-Sorte ausreichen könnte.

$ sort -t, -k2 -n unsorted.txt 
name,21,male 
name,24,male 
name,25,female 
name,27,female 

Wo die Befehlszeile args sind:

-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) 

Wenn Sie eine Perl-Lösung wollen, ist es in qx wickeln() ;-)

3

das ursprüngliche Plakat für kein Dritter gefragt Module (was ich von CPAN nichts verstehe). Während dies eine Einschränkung ist, die Ihre Fähigkeit, guten modernen Perl-Code zu schreiben, auf schreckliche Weise einschränkt, ist es in diesem Fall möglich, das (Kern-) Text :: ParseWords-Modul anstelle des (nicht zum Kern gehörenden) Text :: CSV zu verwenden. Also, wenn wir uns stark von Alans Beispiel leihen, erhalten wir:

#!/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 
Verwandte Themen