Gio, 18 Maggio 2006 - 08:13
Inviato da: Marco Coïsson
Una hash, come abbiamo già avuto modo di discutere, è una specie di array, in cui però l'indice usato per la catalogazione non è numerico, ma è una stringa di caratteri; si effettua allora un immagazzinamento di informazioni sotto forma di chiave-valore, che in qualche misura assomiglia al modo con cui si organizza il più rudimentale dei database. Se state pensando a database complessi, relazionali, professionali, siete fuori strada: le funzionalità di database che Perl offre automaticamente, senza appoggiarsi a prodotti esterni, sono estremamente rudimentali, ma in alcune situazioni possono essere sufficienti.
Aprite il vostro editor di testo preferito e digitate il seguente codice:
#!/usr/bin/perl
while()
{
my $scelta=&SceltaMenu;
if($scelta==1)
{
&InserisciNota;
}
elsif($scelta==2)
{
&MostraNote;
}
elsif($scelta==3)
{
&CancellaNote;
}
elsif($scelta==0)
{
last;
}
}
sub SceltaMenu
{
print "\n";
print "1.\tInserisci una nuova nota\n";
print "2.\tMostra le note\n";
print "3.\tCancella note\n";
print "\n";
print "0.\tEsci\n";
print "\n";
print "Inserisci la tua scelta: ";
return <STDIN>;
}
sub InserisciNota
{
print "\n";
print "Inserisci una nuova nota: ";
my $nota=<STDIN>;
my $data=&InserisciData;
&AggiornaDatabase($data,$nota);
}
sub MostraNote
{
print "\n";
my $data=&InserisciData;
my $nota=&LeggiDatabase($data);
print "\n";
print "Note per $data: $nota\n";
}
sub CancellaNote
{
print "\n";
my $data=&InserisciData;
&AggiornaDatabase($data,"");
}
sub InserisciData
{
my $data=`date +"%d-%m-%Y"`;
chomp $data;
print "Data [$data]: ";
my $d=<STDIN>;
chomp $d;
$data=$d if $d ne "";
return $data;
}
sub AggiornaDatabase
{
my ($d,$n)=@_;
dbmopen(my %DB,"note",0644) or die "Impossibile accedere al database!\n";
if($d eq "")
{
delete($DB{$d})
}
else
{
$DB{$d}=$n;
}
dbmclose(%DB);
}
sub LeggiDatabase
{
my ($d)=@_;
dbmopen(my %DB,"note",0644) or die "Impossibile accedere al database!\n";
my $n=$DB{$d};
dbmclose(%DB);
return $n;
}
Salvatelo col nome che volete e rendetelo eseguibile. Se provate ad eseguirlo, il funzionamento dovrebbe essere abbastanza chiaro: un menu vi permette di optare per l'inserimento, la visualizzazione o la cancellazione di una nota. Le note sono identificate per data (in realtà la chiave può essere una stringa qualunque, di default viene proposta la data corrente, se non si scrive niente la proposta di default viene accettata), e per semplicità una sola nota per ogni data è consentita. Il database contenente le note inserite viene mantenuto da un'esecuzione all'altra del programma, ovvero i dati che inserite vengono in qualche modo salvati su disco. Complimenti, avete realizzato un rudimentale "blocco note".
Il funzionamento, ora che sappiamo molte cose di Perl, è a ben vedere piuttosto semplice. Il programma si suddivide in unità logiche identificate da subroutine, tutte opportunamente richiamate all'interno di un ciclo while senza fine, che si interrompe grazie ad un'istruzione last solo quando l'utente sceglie l'apposita voce da menu. I nomi delle subroutine dovrebbero indicare chiaramente il loro compito, che comunque analizziamo nel dettaglio.
SceltaMenu() è la subroutine che si limita a proporre a schermo l'elenco delle voci del menu; una chiamata all'operatore diamante associato all'input da tastiera (STDIN) cattura ciò che l'utente ha digitato, restituendolo al blocco while chiamante che immagazzina il risultato nella variabile $scelta. Questa è usata per stabilire quali subroutine chiamare;
InserisciNota() è la subroutine che si incarica di richiedere all'utente di inserire il testo di una nuova nota, sempre con <STDIN>, e la data a cui associare la nota stessa, chiamando la subroutineInserisciData(); la memorizzazione vera e propria della coppia chiave-valore (ovvero data-nota) nel database è effettuata nella subroutineAggiornaDatabase();
MostraNote() è la subroutine che, richiesta la data da usare come chiave mediante una chiamata a InserisciData(), richiama successivamente LeggiDatabase() per ricavare la nota associata, producendo poi l'output corretto;
CancellaNote() è la subroutine che, nuovamente richiesta la data da usare come chiave, provvede a richiamare la AggiornaDatabase usando la stringa vuota come testo per la nota;
InserisciData() è la subroutine che richiede la data per cui l'utente desidera inserire, visualizzare o cancellare la nota; come dicevamo, in realtà è la chiave del database, e non è necessario che sia una data. L'uso del comando date tra virgolette oblique consente di ottenere una stringa di testo con la data corrente da usare come valore di default, qualora l'utente rispondesse alla richiesta di input (proposta col solito <STDIN>) semplicemente premendo "invio";
AggiornaDatabase() è la subroutine che si preoccupa di scrivere sul database; esso è un realtà una normalissima hash, che però viene inizializzata mediante la funzionedbmopen(); essa come primo argomento richiede quale hash si desideri utilizzare (%DB nel nostro caso; si noti che, per convenzione, i nomi delle hash usate come database si scrivono tutti in maiuscolo, anche se non è obbligatorio), come secondo argomento richiede il nome del database (note nel nostro caso), che poi (a meno dell'estensione, che Perl aggiungerà automaticamente) sarà il nome del file su cui viene memorizzato, su disco, il database stesso, e come terzo argomento un codice numerico (in formato ottale, quindi con uno zero come primo carattere e senza virgolette di sorta) identificante i permessi di accesso al file; rimandando ai molti approfondimenti comparsi su Tevac e sul suo Forum in merito all'argomento dei privilegi o dei permessi sui file, ricordo che il codice 0644 dà accesso di lettura e scrittura al proprietario del file, e di sola lettura a tutti gli altri utenti; un altro codice molto usato è 0755 che dà permessi di lettura, scrittura ed esecuzione del file al proprietario, e di lettura ed esecuzione a tutti gli altri utenti. Una volta che la hash%DB è stata inizializzata mediante dbmopen() essa, dal punto di vista del programmatore, è assolutamente indistinguibile da qualunque altra hash; l'accesso ai suoi valori sarà fatto con i soliti modi, ma le modifiche verranno immediatamente applicate non già in memoria, ma sul file su disco specificato a secondo argomento di dbmopen(); da notare che se questo file non esiste, esso verrà creato; la cancellazione di una voce dalla hash non si fa associanto la stringa nulla "" alla rispettiva chiave, ma eliminando la chiave stessa con l'uso della funzione delete. Infine, una chiamata a dbmclose() passando ad argomento la hash%DB chiude l'accesso al database;
LeggiDatabase() è la subroutine che apre, sempre con dbmopen(), il database, lo importa in una hash, e usa la chiave fornita ad argomento per estrarre e restituire la nota ad essa associata.
Nella sua semplicità, questa gestione automatica di database sotto forma di hash soffre di due difetti: è ben lungi dall'essere un vero database, e permette di associare un solo valore per ogni chiave. Al primo difetto non si può porre rimedio, ma al secondo fortunatamente sì: Perl infatti offre due funzioni, pack() e unpack() il cui compito è convertire una serie di valori in un'unica stringa, e viceversa, da usare come valore in una hash, sia che essa sia normale, sia che essa sia associata ad un file. La sintassi è semplice:
my $stringa=pack("a20 i f","Marco Coisson",32,1.81);
Questa funzione accetta, come primo argomento, una stringa di formato; essa è costituita da una sequenza di lettere (eventualmente seguite da numeri) separate da spazi. Una lettera a indica una stringa di testo, composta al massimo da tanti caratteri quanti sono indicati nel numero immediatamente successivo (così a20 indica una stringa avente al massimo 20 caratteri), una i indica un numero intero, una f indica un numero decimale; ci sono moltissime altre lettere utilizzabili per indicare il formato, per maggiori informazioni digitate man perlfunc dal Terminale e preparatevi ad una lunga lettura. Così, stabilito il formato, gli argomenti successivi corrisponderanno ad uno ad uno alle varie lettere di formato riportate nella stringa a primo argomento. La funzione pack(), allora, prende, nell'esempio riportato, una stringa di testo avente al massimo 20 caratteri (il mio nome), un numero intero (la mia età) e un numero decimale (la mia altezza in metri) e li "impacchetta" in modo che siamo memorizzati all'interno di un'unica variabile, $stringa nel nostro esempio. Potremo così usare questa variabile come valore di una hash, normale o associata ad un file, per memorizzare più informazioni sotto un'unica chiave. Quando si tratterà di recuperare queste informazioni, la funzione unpack() svolgerà egregiamente il suo compito:
my ($nome,$eta,$altezza)=unpack("a20 i f",$stringa);
Essa restituisce una lista, associabile direttamente a delle variabili racchiuse tra parentesi tonde e separate da virgole, o ad un'array, in ragione di una variabile per ogni elemento che fa parte del formato specificato. Va da sé che è indispensabile che la stringa di formato usata con unpack() sia esattamente la stessa che è stata usata con pack(), se no i risultati saranno quanto meno bizzarri!