Ho risolto alla maniera "Pascal" puro: ho provato cosi' e mi pare che giri perfettamente,
alla conferma della InputBox richiamo questa function; se poi avere suggerimenti e
miglioramenti sono ben accetti.
Grazie
function CeckNumber(Numero:string;var NumeroCurr:Currency):Boolean;
const MaxCar=20;
var NumeroVirgole,i:integer;
ArrCar:array[1..maxcar] of string;
ArrBool:array[1..maxcar] of boolean;
LunghezzaNumero:integer;
begin
CeckNumber:=true;NumeroVirgole:=0;NumeroCurr:=0;
LunghezzaNumero:=length(Numero);
for i:=1 to maxcar do ArrCar[i]:='';
for i:=1 to maxcar do ArrBool[i]:=true;
for i:=1 to LunghezzaNumero do ArrCar[i]:=copy(Numero,i,1);
for i:=1 to LunghezzaNumero do if ArrCar[i]='.' then ArrCar[i]:=',';
for i:=1 to LunghezzaNumero
do begin
if (ArrCar[i]<>'1')and(ArrCar[i]<>'2')and(ArrCar[i]<>'3')and(ArrCar[i]<>'4')
and(ArrCar[i]<>'5')and(ArrCar[i]<>'6')and(ArrCar[i]<>'7')and(ArrCar[i]<>'8')
and(ArrCar[i]<>'9')and(ArrCar[i]<>'0')and(ArrCar[i]<>',') then ArrBool[i]:=false;
end;
if ArrCar[1]='-' then ArrBool[1]:=true;
// conto quante virgole sono inserite se superano 1 il valore immesso è errato
for i:=1 to LunghezzaNumero do if ArrCar[i]=',' then inc(NumeroVirgole);
if NumeroVirgole>1 then CeckNumber:=False
else for i:=1 to LunghezzaNumero do if ArrBool[i]=false then CeckNumber:=false;
Numero:='';
for i:=1 to LunghezzaNumero do Numero:=Numero+ArrCar[i];
if CeckNumber then NumeroCurr:=strtofloat(Numero);
if Not CeckNumber then begin
NumeroCurr:=0;
showmessage('Valore numerico errato');
end;
end;
:)
Puntatori ... questi sconosciuti ;)
Faccio un esempio.
Non so nemmeno se compila, ma serve per capire l'idea che ho in testa ;)
function parseItalianNumber(number: PChar): currency;
function parseIntegerNumber(var number: PChar): integer;
begin
Result := 0;
while number^ in ['0', '9'] do
begin
Result := Result * 10 + (Ord(number^) - Ord('0'));
Inc(number);
if (number = '.') then
Inc(number);
end;
end;
var
parteIntera: integer;
parteDecimale: integer;
posizioniDecimali: integer;
begin
posizioniDecimali := 0;
parteDecimale := 0;
parteIntera := parseIntegerNumber(number);
if (number^ = ',') then
begin
inc(number);
posizioniDecimali := length(number);
parteDecimale := parseIntegerNumber(number);
end;
Result := parteIntera + (Math.power(10, -posizioniDecimali) * parteDecimale);
end;
Separando la parte intera da quella decimale, si può eseguire un parsing più agevole.
Questa funzione fa la conversione di un numero "stringa" in un numero "numerico".
Contrinua fintanto trova cifre comprese tra 0 e 9.
Ad ogni posizione aumenta il valore restituito facendo il calcolo del valore della cifra trovata.
Se trova un '.' come separatore delle migliai, lo ignora.
function parseIntegerNumber(var number: PChar): integer;
begin
Result := 0;
while number^ in ['0', '9'] do
begin
Result := Result * 10 + (Ord(number^) - Ord('0'));
Inc(number);
if (number = '.') then
Inc(number);
end;
end;
Esempio di come usarla:
Prendo la parte intera della stringa fornita.
Controllo se sono arrivato alla ',' e poi passo a parsare la parte decimale.
Prima di partire mi calcolo il numero di cifre (mi servono come 10 alla - x).
Parso la parte decimale e combino le due parti numeriche.
var
parteIntera: integer;
parteDecimale: integer;
posizioniDecimali: integer;
begin
posizioniDecimali := 0;
parteDecimale := 0;
parteIntera := parseIntegerNumber(number);
if (number^ = ',') then
begin
Inc(number);
posizioniDecimali := length(number);
parteDecimale := parseIntegerNumber(number);
end;
Result := parteIntera + (Math.power(10, -posizioniDecimali) * parteDecimale);
end;
Poi ci sarebbero da implementare tutti i controlli di validità del numero fornito.
Punti per le migliaia che non sono a 3 caratteri di distanza. Doppie virgole.
Caratteri che non sono numerici ;)
Ci si può sbizzarrire come si vuole nei controlli..
Se non ti va di fare l'elevazione a potenza puoi fare 2 funzioni per il parsing.
Quella che moltiplica (parte intera) e quella che divide (parte decimale).
Ma tutti quegli array mi hanno solo confuso e non mi è chiaro a cosa servono ;)
Spero vada bene come traccia.
Stilgar
PS:
Per i numeri negativi si può migliorare in questo modo :
var segno : integer;
parteIntera: integer;
parteDecimale: integer;
posizioniDecimali: integer;
begin
segno := 1;
posizioniDecimali := 0;
parteDecimale := 0;
if (numero^='-') then
begin
segno := -1;
inc(number);
end;
parteIntera := parseIntegerNumber(number);
if (number^ = ',') then
begin
Inc(number);
posizioniDecimali := length(number);
parteDecimale := parseIntegerNumber(number);
end;
Result := segno * parteIntera + (Math.power(10, -posizioniDecimali) * parteDecimale);
end;
Spero di poter essere ancora di aiuto, mostrando due metodi che io uso, uno consiste nel verificare la sintassi di un numero in virgola mobile ed un metodo che intercetta le eccezioni.
Metodo 1
function CheckNumber(Numero:ansistring;var NumeroCurr:Currency):Boolean;
var i,punti:byte;
valido:boolean;
begin
valido:=true;
punti:=0;
for i:=1 to length(numero) do
begin
if numero[i]='.' then numero[i]:=',';
if numero[i] in ['0'..'9',',']
then begin
if numero[i]=',' then inc(punti);
end
else valido:=false;
end;
if punti>1 then valido:=false;
if length(numero)=0 then valido:=false;
if (numero[1]=',') or (numero[length(numero)]=',') then valido:=false;
if valido then numerocurr:=StrToFloat(numero);
result:=valido;
end;
Questo metodo converte gli eventuali punti in virgole e verifica che la stringa contenga solo numeri e virgole. Inoltre conta anche le virgole, perchè qualcuno potrebbe inserire più punti o virgole.
Se la sintassi del numero è corretta, procede alla conversione da stringa a valore numerico.
Metodo 2
function CheckNumberEx(Numero:ansistring;var NumeroCurr:Currency):Boolean;
var valido:boolean;
begin
valido:=true;
Numero:=AnsiReplaceText(numero,'.',',');
try
Numerocurr:=StrToFloat(Numero);
except
valido:=false
end;{try}
result:=valido;
end;
Questo metodo richiede che nell'elenco delle unità nella sezione USES sia inserita anche STRUTILS
Dal codice si capisce che la prima riga sostituisce i punti con le virgole, successivamente esegue la conversione da stringa a valore numerico. Se si verifica un errore, il programma non lo mostra, ma esegue semplicemente quello che c'è tra except e end;{try}
Quando si esegue un programma con try nell'IDE del lazarus, l'IDE comunque mostra una notifica dell'eccezione, ma se il programma viene eseguito con il doppio click dell'exe, la finestra dell'eccezione non verrà mostrata.