Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: Nicola - Novembre 07, 2016, 10:17:00 pm

Titolo: Routine per avere numeri da stringhe
Inserito da: Nicola - Novembre 07, 2016, 10:17:00 pm
Ci sono delle routine nella libreria che mi permettano da una InputBox (che restituisce una stringa) il controllo e trasformazione dell'input in valore Currency? ; mi spiego:
la routine dovrebbe controllare che non vi siano caratteri alfabetici, che inserire un punto
equivalga a virgola ed infine controllata la correttezza dei caratteri immessi mi dia come
output un numero Currency.
Grazie
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Stilgar - Novembre 08, 2016, 01:02:36 am
Il tipo currency è un numero a virgola mobile.
Hai provato ad usare la property mask?
Ci sono dei componenti con caratteristiche simili a quelle che cerchi.
Controlla nella paletta dei componenti 😊
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Nicola - Novembre 08, 2016, 10:43:21 pm
Ho provato ad usarlo anche leggendo quanto riportato qua http://www.lazaruspascal.it/index.php?topic=278.0,
ma per inserire valori in Euro, non mi sembra molto pratico,
almeno per quanto concerne le mie conoscenze.
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: bonmario - Novembre 09, 2016, 08:07:22 am
Ciao,
spero di non aver capito male la domanda.
Supponiamo che l'utente scriva il valore in una TEdit, puoi usare l'evento "OnKeyUp" della TEdit, per controllare il carattere immesso e,
- se non è un numero o un punto o una virgola, scartarlo
- se è un punto, sostituirlo con una virgola.

Se non ricordo male, per far sì che l'evento "OnKeyUp" della TEdit sia visto, devi settare nel form la proprietà KeyPreview a True.

Ciao, Mario
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Nicola - Novembre 09, 2016, 11:08:30 am
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

Codice: [Seleziona]
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;
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: nomorelogic - Novembre 09, 2016, 06:44:16 pm
ciao
non so se sono arrivato tardi, ma ho trovato questa funzione

http://rosettacode.org/wiki/Determine_if_a_string_is_numeric#Pascal (http://rosettacode.org/wiki/Determine_if_a_string_is_numeric#Pascal)
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Nicola - Novembre 09, 2016, 10:30:17 pm
Be intanto ti ringrazio per la segnalazione;
la funzione va quasi bene ma la virgola non viene accettata.
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Stilgar - Novembre 09, 2016, 11:26:11 pm
 :)
Puntatori ... questi sconosciuti ;)

Faccio un esempio.
Non so nemmeno se compila, ma serve per capire l'idea che ho in testa ;)

Codice: [Seleziona]
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.
Codice: [Seleziona]
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.

Codice: [Seleziona]
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
       
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Stilgar - Novembre 09, 2016, 11:30:43 pm
PS:
Per i numeri negativi si può migliorare in questo modo :

Codice: [Seleziona]
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;
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Nicola - Novembre 10, 2016, 11:17:53 am
Stasera la provo, volevo solo spiegarti il principio per cui ho utilizzato gli array:
prendo il numero in formato stringa ed ogni carattere lo immetto in un array
in parallelo ho un array di tipo boolean tutto settato a true;
Per prima cosa trasformo se presente il carattere '.' in ',' cosi posso immettere i dati
da qualsiasi tastiera (non come con google spreadsheet che non accetta come virgola
il punto);
inizio la scansione del primo carattere se è un numero ok altrimenti l'array parallelo va a false
in corrispondenza;
terminata la scansione se un solo elemento dell'array booleano è false allora  non è un numero.
Mi pare che funzioni bene e anche concettualmente corretta, vorrei però una vostra opinione
(ad esempio: la tua soluzione è una c.....a pazzesca  ;D)
Nicola
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Stilgar - Novembre 10, 2016, 08:10:20 pm
Ciao Nicola.
Nessuna soluzione è una caga... pazzesca.
Ci sono quelle più veloci e quelle meno veloci.
Se usi le eccezioni puoi dare un'informazione aggiuntiva all'utilizzatore della tua funzione.
Se "spacca" vuol dire che non ha passato un numero valido.
Se restituisce il valore vuol dire che è un numero valido.

Le eccezioni sono un canale "parallelo" di comunicazione. Se scattano vuol dire che ci sono errori.
Basta non abusarne ;)

 
Citazione
terminata la scansione se un solo elemento dell'array booleano è false allora  non è un numero.
Cmq. Al posto di un array per tenere lo stato, non sarebbe più efficente usare una singola variabile booleana?
Tanto basta che ci sia un valore degli n che salvi a farti capire che non è un numero.
Citazione
Mi pare che funzioni bene e anche concettualmente corretta
Se funziona bene non ci sono problemi.
Che sia concettaualmente corretta, non so cosa dirti. E' un'affermazione che non capisco :)
Sarà che da vecchio programmatore del "concettuale" non so come farlo stare dentro al processore :p

A parte gli scherzi.
Usare i puntatori potrebbe aprirti nuove strade.
Iniziare a fare il parsing con i puntatori per scoprire se è un numero o meno, potrebbe portarti a scrivere un parser sintattico. ;)
Gli array sono comodi, ma a volta mi stanno strettini ;) Preferisco di gran lunga i puntatori :)

Detto questo lascio la parola anche agli altri :)

Stilgar
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Nicola - Novembre 10, 2016, 11:44:51 pm
Gentilissimo Stilgar
non so se questo sia il luogo appropriato per rispondere in quanto andrei un po OT;
intanto ti ringrazio per avermi ricordato che esistono anche i puntatori e mi si è RI-aperto
un mondo che avevo abbandonato (per lavoro) nel 1986. Stavo cercando
di implementare un applicativo per la gestione dei cespiti della mia azienda (ovviamente come avrai capito dal tono delle mie domande a livello amatoriale per pura passione), e
nel farlo utilizzo spesso gli array che potrei tranquillamente sostituire con i puntatori,
(almeno cosi' ricordo). Ora per "digerire" la tua versione della funzione mi sa che dovrò
applicarmi piu' del previsto, soprattutto per capirla appieno. Vorrei ringraziare ancora
una volta tutti quanti gli utenti del forum che perdono tempo per le mie domande da
nubbio.
Nicola
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Stilgar - Novembre 11, 2016, 12:37:50 am
Ciao Nicola.
Alcuni colleghi avrebbero da ridire sul "gentilissimo" :)
Vedo che siamo veneti tutti e due ... diciamo che ho il "va in mona" facile :)
Per non dire che mi lascio andare a qualche intercalare tipico di Merghera ;) E non proprio lo "sbicio" o simili :) Quello originale insomma 

Per il resto, se hai bisogno chiedi pure.
Il forum serve per questo :)

Stilgar
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: MadPascal - Novembre 11, 2016, 06:37:45 pm
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

Codice: [Seleziona]
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

Codice: [Seleziona]
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.
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Nicola - Novembre 11, 2016, 10:00:00 pm
Vi dico solo che per "colpa" vostra sto ripassando puntatori, liste dinamiche  e pile   :'( :'( :'(
Gli esempi sono entrambi efficaci con spunti utili per approfondire il problema da punti di vista diversi.
Ora vado un po' OT, ho scaricato alcuni PDF per un ripasso dei puntatori fra cui una dispensa del Prof. LUCIO VARAGNOLO mio prof. di informatica al Sarpi di Venezia
nel periodo 83-85, quasi mi metto a piangere....
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: MadPascal - Novembre 11, 2016, 10:19:44 pm
Perchè scomodare puntatori, liste dinamiche  e pile. Credo sia piuttosto esagerato per una operazione semplice come questa.
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Nicola - Novembre 11, 2016, 11:34:39 pm
In effetti non è correlato al problema del topic, ma solo al fatto che Stilgar ha nominato i
puntatori e mi son reso conto che un ripasso male non può fare, prima o poi torneranno
utili.
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Legolas - Novembre 12, 2016, 12:12:05 pm
Vedo che siamo veneti tutti e due ... diciamo che ho il "va in mona" facile :)

http://www.youtube.com/watch?v=qrf2eQSbPE8

 ;D
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: nomorelogic - Novembre 12, 2016, 08:49:44 pm
Vi dico solo che per "colpa" vostra sto ripassando puntatori, liste dinamiche  e pile   :'( :'( :'(

non voglio rompere le uova nel paniere, studiare (e ristudiare) questi argomenti è molto importante

volevo però segnalare che quelli di fpc hanno realizzato diverse classi per gestire pile, piste stack e quant'altro
affiancherei lo studio di cui sopra a queste unit
http://www.freepascal.org/docs-html/fcl/contnrs/index-4.html (http://www.freepascal.org/docs-html/fcl/contnrs/index-4.html)
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Nicola - Novembre 14, 2016, 02:23:19 pm
Mi studierò anche questi  :-[
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Stilgar - Novembre 14, 2016, 09:02:51 pm
@Legolas ... ma va in m .... na hahahahahahaha
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: nomorelogic - Novembre 15, 2016, 07:46:53 pm
bello il video Legolas :D

soprattutto la spiegazione di "quì va tutto in mona" :D :D :D
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Avogadro - Novembre 15, 2016, 09:28:29 pm
Interessante.
Aggiungo che oltre alla funzione Val, che resituisce un numero da una stringa ( e 0 se non è un numero valido), a corredo in lazarus c'è un parser  molto efficente che personalemente uso per tutti i problemi di questo tipo (esempio mettere una espressione numerica in una casella di testo ed ottenere il valore)

questo il link http://wiki.freepascal.org/How_To_Use_TFPExpressionParser

Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Stilgar - Novembre 16, 2016, 12:08:52 pm
Ho letto la documentazione della funzione che menzionavi.
Molto carina :)
Terrò presente in futuro ;)

Stilgar
Titolo: Re:Routine per avere numeri da stringhe
Inserito da: Legolas - Novembre 22, 2016, 05:17:59 pm
@Legolas ... ma va in m .... na hahahahahahaha

 ;D

bello il video Legolas :D

soprattutto la spiegazione di "quì va tutto in mona" :D :D :D

Non sono veneto, ma il video mi sembra che getti finalmente luce sul mistero della parola "mona"  :D