Written by xinyimanposted in Lazarus 1.0 Ottobre 24, 2011, 11:47:00 pm20452 ViewsRating: 0 (0 Rates)Print
Quante volte è capitato di dirsi, però ora che ho fatto il programma se lo rendessi multilingua potrei distribuirlo in numero maggiore! Bene questo tutorial spiega i passi per rendere il vostro software multilingua in maniera semplice e multipiattaforma. Tutto questo con l'ausilio dei file INi.
Bene per prima cosa necessitiamo di un file ini che chiameremo lingue.ini (nella stessa cartella dell'eseguibile), così strutturato.
[Lingue]
; in questa zona dichiaro le lingue che andrò ad implementare nel mio programma
Numero=2
Lingua1=italiano
Lingua2=inglese
[italiano]
;notare che italiano è tutto minuscolo per essere uguale alla lingua dichiarata sopra nella sezione Lingue
;per non creare problematiche di alcun tipo in sistemi operativi case-sensitive
Lbl_Nome=Nome
Lbl_Cognome=Cognome
[inglese]
Lbl_Nome=Name
Lbl_Cognome=Surname
Come seconda cosa andiamo ad analizzare un po di codice Free Pascal per interagire con tale file INI. Creiamo un nuovo progetto con una form che si chiama Form1 e aggiungete tre componenti, una TComboBox e due TLabel. Ecco come sarà il sorgente
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls
,INIFiles {libreria per usare i file ini}
;
{ In fase di caricamento delle form inserisco nella combobox le possibili lingue inserite nel file ini}
procedure TForm1.FormCreate(Sender: TObject);
Var
INI:TINIFile;
Numero, i: integer;
begin
INI := TINIFile.Create(Application.Location + 'lingue.ini');
Numero := StrToInt(INI.ReadString('Lingue','Numero',''));
for i:=1 to Numero do
begin
Cmb_Lingue.Items.Add(INI.ReadString('Lingue','Lingua' + IntToStr(i),''));
end;
Ini.Free;
Cmb_Lingue.Text:=Cmb_Lingue.Items.Strings[0];
CambiaLingua();
end;
{ Al cambiamento del contenuto della ComboBox cambio i valori delle label }
procedure TForm1.Cmb_LingueChange(Sender: TObject);
begin
CambiaLingua();
end;
procedure TForm1.CambiaLingua();
var
i: integer;
INI:TINIFile;
Lingua: string;
begin
Lingua:=Cmb_Lingue.Text;
INI := TINIFile.Create(Application.Location + 'lingue.ini');
for i:=0 to Form1.ComponentCount-1 do
begin
Form1.Controls[i].Caption:=INI.ReadString(Lingua,Form1.Components[i].Name,'');
end;
Ini.Free;
Cmb_Lingue.Text:=Lingua;
end;
end.
Il codice è abbastanza semplice, è facile dedurne il funzionamento compilandolo. Se avete soluzioni migliori o alternative (ma sempre il più possibile multipiattaforma) mandatemeli tramite messaggio, io provvederò a pubblicarli.
Per funzioni/procedure ricorsive si intendono
funzioni/procedure che al loro interno richiamano loro stesse. Questo
tipo di logica che viene chiamata logica ricorsiva permette un codice
più pulito e più facilmente leggibile. Prendiamo in considerazione
l'esempio principe per capire la ricorsione: il fattoriale.
{La
funzione che permette la fatturazione di un numero}
function
Fatt(numero: integer): longint;
begin
if
numero=1 then
Fatt:=numero
else
Fatt:=(numero)*(Fatt(numero-1));
end;
Come si può vedere la funzione in questione si chiama
Fatt e prende come parametro un numero intero. Ma se andiamo ad
analizzare il codice possiamo trovare la seguente riga come anomala:
Fatt:=(numero)*(Fatt(numero-1)); perché
ritorniamo come valore la moltiplicazione del parametro per il
risultato di una chiamata a se stessa. Cioè all'interno della
funzione Fatt esiste una chiamata alla funzione Fatt. Questo modo di
usare le funzioni/procedure è molto pulito e facile da interpretare,
ma ad esempio rispetto ad un ciclo all'interno di una funzione occupa
più spazio in memoria. Per creare delle buone funzioni/procedure
ricorsive è necessario che le stesse non entrino in un ciclo
infinito, per ciclo infinito si intende che non si verifica mai la
condizione per uscire dal ciclo. Per esempio immaginiamo di aver
dichiarato come variabile globale, quindi visibile anche all'interno
di funzioni, un vettore lungo 50 celle di nome esempioric, e vogliamo
sapere quante celle sono valorizzate con il numero 20; dovremmo
scrivere la seguente funzione non ricorsiva:
function
Conteggia20(): integer;
var
i:
integer;
cont:
integer;
begin
cont:=0;
for
i:=0 to 49 do
begin
if
(esempioric[i]=20) then
begin
cont:=cont+1;
end;
end;
Conteggia20:=cont;
end;
Così facendo abbiamo una funzione che cicla fino a fine
vettore e ogni qual volta incontra un valore 20 all'interno del
vettore incrementa di uno la variabile cont, che corrisponde al
risultato che vogliamo ottenere. Ma questo non è l'unico modo per
scrivere questa funzione, perché con la ricorsione possiamo
ottenere:
function
Conteggia20Ric(indice: integer): integer;
begin
if
indice>=50 then
Conteggia20Ric:=0
else
begin
if (esempioric[indice]=20) then
Conteggia20Ric:=1+Conteggia20Ric(indice+1)
else
Conteggia20Ric:=Conteggia20Ric(indice+1);
end;
end;
Come si può evincere dall'esempio non esiste un ciclo
all'interno della funzione stessa, ma semplicemente la funzione
analizza il valore della prima cella e poi se non si trova all'ultima
cella controlla tramite una chiamata a se stessa (e qui entra in
gioco la ricorsione) il valore della cella successiva. C'è da notare
anche una altra cosa che la seconda funzione scritta (quella
ricorsiva) ha un parametro, che corrisponde alla cella del vettore
che deve analizzare, quindi quando dal programma principale
richiameremo la funzione bisogna passargli come parametro il valore
0.
Per libreria si intende una raccolta di procedure e
funzioni. L'uso delle librerie è molto frequente perché permette di
suddividere la scrittura del codice da uno a più file, in modo da
rendere il codice più leggibile, meglio strutturato e
riutilizzabile. Per realizzare una nuova libreria basta andare su
FILE → NUOVA UNIT dopodichè comparirà questo codice:
unit
Unit2;
{$mode
objfpc}{$H+}
interface
uses
Classes, SysUtils;
implementation
end.
Dove unit è una parola riservata che identifica
l'inizio della nostra libreria e Unit2 rappresenta il nome della
nostra libreria. Nel nostro caso dobbiamo cambiare Unit2 con il nome
che vogliamo dare alla nostra ipotetica libreria, ipotizziamo
FunzioniRiciclabili, si noti che quando si va a salvare la libreria
bisogna dargli lo stesso nome della unit.
Dopodichè esiste un commento che ignoriamo per andare a
vedere la parola riservata interface, dove all'interno di questo
segmento di codice possiamo dichiarare costanti, variabili, funzioni
e procedure visibili dall'esterno della libreria stessa.
Segue poi la parola riservata uses dove all'interno di
questo segmento di codice dobbiamo inserire le librerie che ci
servono per poter compilare le nostre funzioni e procedure.
Infine esiste il blocco implementation end. che
contengono l'implementazione del codice, ovvero dove dobbiamo
scrivere le procedure e le funzioni per intero.
Vediamo ora un esempio pratico, questa librerie che
segue contiene una sola funzione di nome
ContieneCaratteriNonConcessi.
unit
FunzioniRiciclabili;
{$mode
objfpc}{$H+}
interface
function
ContieneCaratteriNonConcessi(MiaStringa: string): integer;
implementation
uses
Crt;
{Mi dice se nella stringa ci sono caratteri
tipo il punto e virgola che non sono ammessi}
function
ContieneCaratteriNonConcessi(MiaStringa: string): integer;
var
i: integer;
lung: integer;
Esci: integer;
begin
lung:=Length(MiaStringa);
i:=1;
Esci:=0;
while ((i<lung) AND (Esci=0)) do
begin
if MiaStringa[i]=';' then
begin
Esci:=1;
end;
i:=i+1;
end;
ContieneCaratteriNonConcessi:=Esci;
end;
end.
Come si può evincere da questo esempio il nome della
libreria è FunzioniRiciclabili e l'unica funzione contenuta al suo
interno lavora sulle stringhe, ovvero in base ad una stringa passata
per parametro che se non contiene il carattere ; allora restituisce
il valore numerico 0, altrimenti restituirebbe 1.
Ora che abbiamo scritto la nostra libreria è importante
riuscire a poterla usare nelle altre unit che compongono il
programma. Per fare ciò bisogna immettere nella sezione uses della
unit in cui vogliamo usare la funzione appena scritta il nome della
libreria appena creata, nel nostro caso sarebbe FunzioniRiciclabili,
e ora possiamo usare la funzione ContieneCaratteriNonConcessi
liberamente.
Fino ad ora abbiamo visto solo programmi che
permettevano il mantenimento di dati in memoria solo fino al termine
dell'esecuzione dello stesso. Mantenere i dati memorizzato oltre
l'esecuzione del programma è possibile tramite l'uso dei file. Per
capire come funzionano analizziamo il seguente esempio.
procedure
LavoriamoConIFile();
var
F: system.Text;
Riga: string;
begin
AssignFile (F,'Test.txt');
rewrite (f);
writeln (f,'Uso dei file1!');
CloseFile (f);
AssignFile (F,'Test.txt');
append (f);
writeln (f,'Uso dei file2!');
CloseFile (f);
AssignFile (F,'Test.txt');
reset
(f);
while
not eof(F) do
begin
readln(f,Riga);
writeln(Riga);
end;
CloseFile (f);
writeln('FINITO!');
end;
Abbiamo dichiarato due variabili, la variabile F che
contiene lo stream al file e la variabile Riga che conterrà i dati
letti dal file. In questo modo:
var
F: system.Text;
Riga: string;
Dopodiché abbiamo detto al programma che vogliamo
aprire un canale di comunicazione con un file che in questo caso si
chiama Test.txt e che il canale in questione si chiama F, e lo
abbiamo fatto in questo modo:
AssignFile (F,'Test.txt');
Però al momento non abbiamo ancora aperto fisicamente
un canale di comunicazione con il file, cosa che facciamo in questa
maniera:
rewrite (f);
Il rewrite non è il solo modo di aprire il canale verso
il file. Ci sono tre modi di farlo e sono:
rewrite: che apre il file in scrittura, se non c'è lo
crea e se esiste con del contenuto ne cancella il contenuto.
reset: che apre il file in sola lettura, e se il file
non esiste restituisce un errore
append: apre il file in scrittura ma senza cancellarne
il contenuto, aggiungendo al fondo del contenuto già esistente.
In seguito abbiamo scritto all'interno del file la
stringa 'Uso dei file1!', in questo modo:
writeln (f,'Uso dei file1!');
al passo successivo abbiamo chiuso in canale il sola
scrittura sul file in questo modo:
CloseFile (f);
Bene così facendo abbiamo creato un file di nome
Test.txt se ancora non esisteva, se invece già esisteva allora lo
abbiamo svuotato. Dopodiché abbiamo inserito la stringa 'Uso dei
file1 !'. Ma il programma non è ancora terminato, e vediamo come
prosegue:
AssignFile (F,'Test.txt');
append (f);
writeln (f,'Uso dei file2!');
CloseFile (f);
Grazie a quello appena imparato possiamo analizzare il
blocco per intero, che apre il canale al file senza svuotarlo (grazie
al comando append) e inserisce in coda la stringa 'Uso dei file2!',
in seguito chiude il canale al file.
Ma il programma ancora non è terminato, analizziamo il
pezzo successivo:
AssignFile (F,'Test.txt');
reset
(f);
while
not eof(F) do
begin
readln(f,Riga);
writeln(Riga);
end;
CloseFile (f);
writeln('FINITO!');
In questo ultimo blocco che andiamo ad analizzare
possiamo subito notare che grazie al comando reset apriamo il canale
al file in sola lettura. Dopodiché entriamo in un ciclo da cui si
esce solo quando si è a fine file. Ogni volta che eseguo una
interazione (ovvero un giro del codice contenuto nel ciclo) leggo una
riga dal file con il comando readln passandogli come parametri il
canale f e una stringa che conterrà la riga stessa e poi la stampo
grazie al comando writeln. Una volta che sono arrivato a fine file
stampo a video la stringa 'FINITO!'.
Da tenere ben presente è la dicitura EOF che significa End Of File e che serve ad identificare la fine del file.
Fino ad ora tutto il codice che abbiamo scritto era in
un blocco solo, ovvero abbiamo scritto tutte le righe una dietro
l'altra, ma solo perché gli esempi visti erano semplici, ma capita e
anche sovente di dover scrivere pezzi di codice che serve
riutilizzare. Le motivazioni possono essere molteplici, ma sovente
conviene utilizzare le procedure e le funzioni piuttosto che scrivere
un monoblocco di codice. Ipotizziamo di dover invertire il valore di
due variabili di nome Valore1 e Valore2 e di dover effettuare questa
operazione sovente all'interno del nostro programma, per poter fare
questa operazione abbiamo bisogno anche della variabile di nome
Valore3. Vediamo ora come andrebbe scritto il codice che effettua
l'inversione dei valori:
Valore3:=Valore1;
Valore1:=Valore2;
Valore2:=Valore3;
Bene ora immaginiamo di doverlo scrivere 10, 100, 1000
volte all'interno del codice, possiamo facilmente capire che il
codice in questione diverrà molto lungo, quindi difficile da gestire
in un secondo momento, e in più il programma diverrebbe molto grosso
senza tenere conto che la variabile di nome Valore3 rimarrebbe
allocata in memoria per tutta l'esecuzione del programma. Mentre ora
immaginiamo di immettere questo codice scritto in una procedura, le
migliorie sono diverse, ad esempio quando la procedura ha finito la
sua vita la variabile Valore3 viene deallocata, il programma occupa
meno spazio e se bisogna fare manutenzione il codice diventa più
leggibile.
Bene fino ad ora abbiamo parlato di procedure e funzioni
come se fossero la stessa cosa, ma fondamentalmente c'è una
differenza tra le due cose, ovvero che le funzioni restituiscono un
valore, mentre le procedure no. Vediamo ora come si dichiara una
procedura e come si dichiara una funzione. Procedura:
procedure
NomeProcedura();
begin
.
.
.
end;
Funzione:
function
NomeFunzione(): tiporestituito;
begin
.
.
NomeFunzione:=valorerestituito;
end;
La prima cosa che bisogna notare è che sia le funzioni
che le procedure hanno un nome che le contraddistingue, per seconda
cosa bisogna notare che entrambe terminano con le parentesi e con il
punto e virgola. Ma le due cose più importanti da notare sono che la
funzione per restituire un valore deve specificare di che tipo è, e
per effettuare la restituzione bisogna attribuire il valore da
restituire al nome della funzione stessa.
Un altra cosa da apprendere quando si tratta l'argomento
procedure e funzioni è il passaggio di parametri. Per passaggio di
parametri si intende il passare delle informazioni alla procedura o
alla funzione. Vediamo un esempio concreto:
Bene come si può notare, all'interno delle parentesi
tonde subito dopo il nome della procedura, è presente la
dichiarazione di tre variabili intervallate da dei punti e virgola.
In questo modo definisco quanti parametri passare alla
funzione/procedura specificando il tipo del dato che gli passo.
I parametri possono essere passati in due modi, per
valore o per referenza. La differenza tra le due è che se sono
passati per valore, il valore stesso non può essere modificato
all'esterno della procedura, se invece il parametro è passato per
referenza allora il valore se viene modificato verrà modificato
sempre nel programma. In pratica i due tipi si differenziano grazie
alla parola riservata var all'interno delle parentesi tonde. Vediamo
un esempio di passaggio di parametri per valore:
procedure
Inverti(var Valore1: integer; var Valore2: integer);
e ricompilare. Una volta eseguite entrambe le versioni è
evidente la differenza tra le due.
N.B.: se per caso si immettesse la riga procedure
Inverti(Valore1: integer; var Valore2: integer); allora
Valore1 sarebbe passato per valore e Valore2 per referenza.
Questo blog non rappresenta una testata giornalistica poiché viene
aggiornato senza alcuna periodicità. Non può pertanto considerarsi un
prodotto editoriale ai sensi della legge n. 62/2001.
Questo sito utilizza cookie, anche di terze parti, per offriti servizi in linea con le tue preferenze. Chiudendo questo banner, scorrendo questa pagina, cliccando su un link o proseguendo la navigazione in altra maniera, acconsenti all’uso dei cookie.