mercoledì 23 aprile 2008

Una implementazione di cercaVert in Perl

In questo articolo presento un semplice script Perl che realizza la funzione cercaVert di Excel operando su un file CSV. L'idea è quella di fornire allo script un file CSV specificandogli di cercare i valori di una colonna in un'altra, e di riportare in un file di uscita i valori che trovano riscontro fra le due colonne.
In altre parole, se il file CSV contiene i seguenti dati di esempio:

luca;marco;maschio;
marco;lucia;femmina;
simone;luca;maschio;


e si specifica di cercare i valori della seconda colonna nella prima, allora si avrà match sui valori marco (riportato nella prima riga, seconda colonna - seconda riga, prima colonna) e luca (riportato nella terza riga, seconda colonna - prima riga, prima colonna), ottenendo in uscita il file:

marco;1;
luca;1;


dove la prima colonna rappresenta il valore cercato e la seconda il numero di occorrenze di tale valore nella prima colonna.

Il funzionamento dello script è abbastanza semplice: oltre al file CSV da analizzare vengono passati come argomenti i numeri delle colonne sulle quali fare match, partendo da 1 per la prima colonna a sinistra. Il programma legge riga per riga il file CSV memorizzando in un hash (occurences) il valore cercato e il relativo numero di occorrenze, che al momento risulta pari a zero. Allo stesso tempo, viene memorizzato in un array (domain) la lista dei valori in cui cercare. In altre parole, se si vuole controllare la colonna 2 sulla colonna 1, allora occurences conterrà tutti i valori della colonna 2, mentre domain i valori della colonna 1. Terminata la lettura del file, e quindi con le due origini dati in memoria, viene fatto un ciclo su tutti i valori memorizzati nell'hash e li si confronta con tutti i valori (che possono essere duplicati) del dominio in cui cercare, incrementando le eventuali occorrenze. Infine si riportano sul file di uscita le chiavi e il numero di occorrenze trovate.

#!/usr/bin/perl

# Programma che emula la funzione cercavert di excel: ricerca un campo di una colonna di un file CSV
# in un'altra colonna.
#
# Argomenti dello script:
# $ARGV[0] = nome del file CSV da leggere (deve essere
# formattato con separatori ';'
# $ARGV[1] = numero della colonna da cui prendere i codici
# $ARGV[2] = numero della colonna in cui cercare i codici
#
# Ad esempio:
#
# cercaVert.pl file.csv 2 1
#
# ricerca i codici della colonna 2 nella colonna 1 del file file.csv


# * Copyright (C) Luca Ferrari 2008
# *
# * This program is free software: you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation, either version 3 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program. If not, see .



# controllo numero argomenti
if( $#ARGV < 3 ){
print "\nUtilizzo: $0 \n";
exit(0);
}


# apro il file da cui leggere e su cui scrivere
open(FILE_CSV, "<$ARGV[0]") || die("\nImpossibile aprire il file $ARGV[0]\n$!\n");
open(FILE_OUT,">$ARGV[3]") || die("\nImpossibile aprire il file $ARGV[3]\n$!\n");
print FILE_OUT "Valore ricercato (colonna $ARGV[1]); numero di occorrenze trovate nella colonna $ARGV[2];\n";
$SRC_COLUMN = $ARGV[1] - 1;
$DST_COLUMN = $ARGV[2] - 1;
print "\nRicerco le occorrenze dei valori della colonna $DST_COLUMN nella colonna $SRC_COLUMN del file $ARGV[0]...\n";


# leggo il file una volta, estrando la colonna sorgente e
# costruisco un hash che contiene tutte le occorrenze trovate, e un
# array che contiene tutti i valori in cui cercare successivamente
$occurences = {};
@domain = ();

while( $line = ){
@parts = split(";", $line);

$srcValue = $parts[ $SRC_COLUMN ];
$dstValue = $parts[ $DST_COLUMN ];

# memorizzo il valore nell'hash
$occurences->{ $srcValue } = 0;

# aggiungo il valore in cui cercare nell'array
$domain[ ++$#domain ] = $dstValue;
} # fine del ciclo while


close(FILE_CSV);

print "\nInizio ricerca fra $#domain valori...\n";
@domain = sort(@domain);
$counter = 0;

foreach $chiave (sort(keys(%$occurences))){
foreach $valore (@domain){
if( $valore eq $chiave ){
$occurences->{$chiave} += 1;
$counter ++;
}
}
}

print "\nTrovati $counter valori uguali...";

# produco in uscita il risultato
foreach $chiave (sort(keys(%$occurences))){
if( defined($occurences->{$chiave}) && $occurences->{$chiave} > 0 ){
print FILE_OUT "\n$chiave;" . $occurences->{$chiave};
}
}


print "\nfinito\n";

Lo script è piuttosto semplice, e l'utilizzo di un hash e di un array consente di tenere solo i valori non duplicati da cercare contro un dominio di valori che possono essere duplicati. Va notato che, tenendo le fonti dati in memoria, in caso di file CSV molto grandi questo script tenderà a consumare notevoli risorse.

1 commento:

fa ha detto...

ciao,
ho visto il tuo script che trovo molto interessante perchè stavo cercando questa funzione in perl ma quando lo avvio con il tuo file di esempio mi da questo errore. mi sapresti aiutare?grazie mille

perl cercaVert.pl Cartel1.csv 2 1
syntax error at cercaVert.pl line 58, near "= )"
syntax error at cercaVert.pl line 69, near "}"
Execution of cercaVert.pl aborted due to compilation errors.