Oggi nel canale perl ci si è chiesto come individuare stringhe arbitrarie ricorrenti in un file (ie: query SQL in un file php).
La risposta di per se sarebbe semplice: si fa una regex che inizia con SELECT e finisce con qualcosa (ie:
e si è a cavallo.
Il grado di difficoltà però aumenta se le stringhe sono su più righe…
Avendo trovato una soluzione che non richiede nemmeno un ciclo, la scrivo poichè ne sono orgoglioso e mi è valsa un ++.
use strict;
use File::Slurp;
my $text = read_file(‘file.php’);
my @queries = $text =~ /("SELECT.*?");/msg
Ogni valore di @queries conterrà uno dei SELECT nel file, siano essi su più righe o meno.
Più breve di così…
Segnalo anche un paio di letture sulle regex che potrebbero venire comode per capire i modificatori /m e /s, che si usano pochino.
Regexp Power by Simon Cozens (1 e 2)
Dovevo trasferire rapidamente i dati da una singola tabella in un db MySQL a quattro distinte – in modo da normalizzare un po’ i dati.
Con perl e DBI la cosa viene facile.
use strict;
use DBI;
[…]
my $dsn = "dbi:mysql:database=$db_name";
my $dbh = DBI->connect( $dsn, $username, $password ) or die $DBI::errstr;
my $sth = $dbh->prepare(
‘SELECT name, surname, birth, address, cap, city, country, phone, email, notes FROM rubrica’
) or die $dbh->errstr;
$sth->execute();
my $ins0 = $dbh->prepare(
‘INSERT INTO persone (nome,cognome) VALUES (?,?)’
) or die $dbh->errstr;
my $ins1 = $dbh->prepare(
‘INSERT INTO indirizzi (id,via,cap,citta,paese) VALUES (?,?,?,?,?)’
) or die $dbh->errstr;
[… ecc, seguono altri statement insert …]
while ( my $row = $sth->fetchrow_hashref() ) {
$ins0->execute( $row->{name}, $row->{surname} );
my $rv = $dbh->last_insert_id(undef, undef, ‘persone’, ‘id’);
$ins1->execute( $rv, $row->{address}, $row->{cap}, $row->{city}, $row->{country} );
[…]
}
La cosa interessante sta in quel last_insert_id che raccatta l’ultimo valore auto incrementato e lo mette in uno scalare, riutilizzabile poi in altri insert per avere le foreign key allineate.
Per convertire una data in timestamp in perl:
1235775600
Attenzione, la data inserita è il 28 febbraio 2009, a mezzanotte.
Il modulo Time::Local fornisce funzioni che sono l’opposto di quelle built-in di perl localtime() e gmtime().
Attenzione ai mesi che vanno da 0 a 11 (gen .. dic).
Mi è capitato troppe volte di trovarmi file XML assolutamente illeggibili per le mani.
Una soluzione, come al solito, arriva da CPAN: XML::Tidy!
Ridi e scherza, questo modulino è in realtà un sacco di codice che parsa alla perfezione i file xml e li riordina ad-hoc.
#!/usr/bin/perl
use strict;
use warnings;
use XML::Tidy;
my $VERSION = 0.02;
my $file_in = $ARGV[0] || die "Usage:\t$0 <input.xml> <output.xml>\n";
my $file_out = $ARGV[1] || ‘output.xml’;
my $tidy_obj = XML::Tidy->new(‘filename’ => $file_in);
$tidy_obj->tidy();
$tidy_obj->write($file_out);
Il mio semplice script prende un file in input da cmdl e sputa fuori un output.xml “raddrizato” (2 whitespace di default come indentazione, se si vuole modificare bisogna dare un arogomento al metodo ->tidy()).
Per ulteriori info.
Nota: La libreria perl sta anche in un pacchetto debian (libxml-tidy-perl) che guarda caso fornisce uno scriptino (/usr/bin/xmltidy) molto simile (quasi identico) al mio.
A forza di spaccarsi la testa sulle cose, ogni tanto se ne esce.
Avendo un database su PostgreSQL, e volendo manipolarlo con strumenti ORM in perl si può usare DBIx::Class (DBIC per gli amici).
DBIC si appoggia a “schemi” di tabelle nel database che gli permettono di interagire col db-server in maniera corretta (e ottimizzata).
Gli schemi sono un po’ pallosi da estrarre, ma a tutto c’è soluzione:
Dopo questo comando nella directory Sim ci sarà un pacchetto Prova.pm (lo schema) che caricherà le classi che rappresentano le tabelle del database XXX (nella dir Sim/Prova/tabella_*.pm).
Le rappresentazioni delle tabelle sono interessanti da guardare.
Da questo momento sarà possibile scrivere script che usino lo schema (Sim::Prova), si connettano attraverso di esso (metodo connect) ed agiscano sui dati del database XXX.
use Sim::Prova;
my $schema = Sim::Prova->connect(‘dbi:Pg:dbname=XXX’, ‘YYY_user’, ‘ZZZ_passwd’, {});
my @all_authors = $schema->resultset(‘Authors’)->all;
foreach (@all_authors) {
print $_->name ."\n";
}
L’oggetto $schema porta la connessione, tramite ->resultset(‘table’) capisce com’è organizzata la tabella nel db e poi accetta i metodi utili per fare qualcosa (CRUD: Create, Retrieve, Update, Delete).
$schema->resultset(‘tabella’)->create( {id => 1, name => ‘Simone’} );
# R
my @rows = $schema->resultset(‘tabella’)->all;
foreach $row (@rows) {
print $_->id ." | ". $_->name ."\n";
}
# U
my $author = $schema->resultset(‘tabella’)->find(1);
# dentro $author c’è il nostro record.
$author->name(‘Mario Rossi’);
# rimpiazza l’attributo "name" di $author con qualcosa di diverso.
# A questo punto il database ha ancora il record non cambiato, ma
# $author contiene un record differente.
$author->update();
# così aggiorno i dati sul db.
# D
# Come update, si identifica il record giusto e…
$author->delete();
Un po’ di basic usage e di manualistica.
E’ un periodo in cui sto lavorando molto con perl e database differenti.
Ho già parlato del modulo DBI, e di quanto esso mi gusti.
Se per caso servisse sapere quali driver DBI mette a disposizione, ecco l’elenco dei database a cui è possibile collegarsi.
AnyData
CSV
DBM
ExampleP
File
Gofer
Pg
Proxy
SQLite
Sponge
mysql
Come si sa (almeno per chi programma in perl), gli hash non hanno un ordine proprio, ma gestiscono solo le coppie chiave-valore.
Di recente ho dovuto inserire in un hash un elenco di chiavi (stringhe) con un conteggio come valore (interi), in pratica una tipica classifica estratta con un while parsando file di testo con una regex.
L’input dei dati è quello tipico:
while(<>) {
my $parsato = /^regex$/;
# …
$hash{$parsato}++;
}
per ritrovarci con un hash che contiene chiavi (il testo regex’ato) e valori (numerici).
Per riordinarli ho deciso di estrarre un elenco di chiavi secondo un certo criterio (i valori numerici dal più alto al più basso), e poi tagliare i primi X valori che mi interessavano.