Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: tito_livio - Dicembre 16, 2024, 06:37:37 pm

Titolo: defaultsettings.decimalseparator cambia da solo
Inserito da: tito_livio - Dicembre 16, 2024, 06:37:37 pm
Ciao a tutti.
Premetto che lavoro con Windows e in tutte le macchine dove girano i miei programmi il separatore è settato nel sistema operativo come virgola.
In alcuni miei programmi, però, tratto dei numeri reali in formato double usando il punto come separatore decimale.

Semplificando, l'utente inserisce dei valori in delle tedit e poi questi dati vengono convertiti da stringa a double ed elaborati.
Una volta che i dati sono stati elaborati, i risultati vengono scritti dal programma in altre tedit.
Nel programma viene settato defaultsettings.decimalseparator:='.' e l'utente inserisce i valori usando il '.' .

Tutto funziona benissimo ma casualmente e come se autonomamente venisse impostata, all'interno del programma, la virgola come separatore decimale.
A questo punto il programma comincia a scrivere i risultati con la virgola e va in errore perché trova i valori dell'utente scritti col punto.

La cosa succede solo su alcuni PC, come se in Windows si verificasse un evento che "comanda" sui settaggi interni del programma.
La cosa mi succedeva anche nei tempi lontani del Delphi, a quei tempi avevo risolto mettendo decimalseparator:='.' in varie parti dell'applicativo, adesso però non funziona.
Avete qualche idea di cosa succede in proposito e come fare per risolvere?
Grazie in anticipo.
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: bonmario - Dicembre 16, 2024, 07:53:30 pm
Ciao,
anni fa mi era successa la stessa cosa, col l'istruzione "TryStrToFloat", poi ho scoperto che la stessa istruzione era stata ridefinita con un parametro in più: "Const FormatSettings: TFormatSettings".
Da allora, chiamo questa col parametro in più, passandogli come ultimo parametro una mia variabile "FormatSettings" contenente i valori che voglio io.

Ciao, Mario
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: quack - Dicembre 16, 2024, 08:29:22 pm
Solitamente invece del componente TEdit uso TFloatSpinEdit così posso leggere o assegnare il valore tramite la proprietà Value e bypasso il problema.
Virgola e punto vengono convertiti nel carattere di sistema automaticamente dopo la digitazione.

Uso invece

  DefaultFormatSettings.DecimalSeparator
  DefaultFormatSettings.DefaultFormatSettings

se devo salvare i risultati in dei file di testo, in questo modo mi assicuro
che la formattazione sia sempre la stessa.

Ciao
qk
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: tito_livio - Dicembre 16, 2024, 08:50:10 pm
Ciao,
anni fa mi era successa la stessa cosa, col l'istruzione "TryStrToFloat", poi ho scoperto che la stessa istruzione era stata ridefinita con un parametro in più: "Const FormatSettings: TFormatSettings".
Da allora, chiamo questa col parametro in più, passandogli come ultimo parametro una mia variabile "FormatSettings" contenente i valori che voglio io.

Si, è vero quello che tu dici, io ho semplificato ma in realtà questi valori double vengono trattati in troppi modi e posti del programma.
Uso anch'io "TryStrToFloat"  ma anche "FloatToStrF", poi queste variabili finiscono in un db o in file di testo o xml, appaiono a video, quindi non vorrei intervenire in così tanti punti del sorgente e in così tanti tipi di conversione.
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: DragoRosso - Dicembre 16, 2024, 09:49:41 pm
La problematica non ha una soluzione generale.

Il carattere di separazione dei decimali dipende da una impostazione, visibile e modificabile nel pannello di controllo relativo all'"Area Geografica". Questa impostazione ovviamente deriva dalla "lingua" o meglio regione di installazione di Windows.

Questo però non vuole dire che settando quel valore tutto vada a posto. Ci sono controlli grafici (sia in LCL che in VCL) e anche delle "routine" che usano delle impostazioni fisse perchè derivate da ormai secolari (per non dire millenarie) usanze primitive.

Quindi due cose si possono fare:

1) come accenavano i precedenti post, impostare una variabile di tipo TFormatSettings e modificare ciò che più a noi va bene-

2) controllare la documentazione dei singoli oggetti che generano numeri in formato stringa per verificare che non ci siano problematiche.

Ovviamente la varibile di cui sopra DOVRA' ESSERE USATA in tutte le funzioni di conversione. Non ci sono alternative.

Per la scrittura nei vari "posti", anche li ci sono poche soluzioni se non prevedere delle tipologie di conversione a priori.

Ad esempio io sui file INI (che ancora uso per alcuni settaggi dei miei programmi) ho derivato la classe TIniFiles e nascosto (override) i metodi originali ReadFloat e WriteFloat esponendo i miei che filtrano i risultati correggendoli e richiamando le funzioni originali (io lavoro con tutte le lingue più improbabili e non posso avere problemi di questo tipo).

Per tutto il resto uso sempre il TFormatSetting della variabile globale nel programma.

Alcuni controlli li tratto a se stanti come accennato (ad esempio la tastiera virtuale) di Delphi.

Altro consiglio: non esporre mai un numero float in stringa a meno che non sia impossibile altrimenti. E' fondamentale ciò.

Ciao
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: tito_livio - Dicembre 16, 2024, 11:03:00 pm
Solitamente invece del componente TEdit uso TFloatSpinEdit così posso leggere o assegnare il valore tramite la proprietà Value e bypasso il problema.
Virgola e punto vengono convertiti nel carattere di sistema automaticamente dopo la digitazione.

Ok con TFloatSpinEdit si può prendere il value e fare i calcoli giusti però essendo un componente che rappresenta un numero "reale" sotto forma di stringa per essere visibile, se cambia (da solo) il decimalseparator lui mi farà vedere il numero con un altro decimal separator.
Comunque è interessante il componente , lo devo approfondire.

  DefaultFormatSettings.DecimalSeparator
  DefaultFormatSettings.DefaultFormatSettings

Questo invece mi è poco chiaro
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: tito_livio - Dicembre 17, 2024, 12:11:14 am
2) controllare la documentazione dei singoli oggetti che generano numeri in formato stringa per verificare che non ci siano problematiche.

Ovviamente la varibile di cui sopra DOVRA' ESSERE USATA in tutte le funzioni di conversione. Non ci sono alternative.

Per la scrittura nei vari "posti", anche li ci sono poche soluzioni se non prevedere delle tipologie di conversione a priori.

Infatti anch'io penso, a questo punto, che non ci siano alternative, però se il problema si manifesta, con lo stesso programma, solo su alcune macchine, allora vuol dire che una problematica c'è ed è propria di quelle macchine.

Altro consiglio: non esporre mai un numero float in stringa a meno che non sia impossibile altrimenti. E' fondamentale ciò.

A volte i float si devono convertire per forza in stringa per tanti motivi. Questi dati in float volte si devono stampare, visualizzare o inviare ad altri sistemi.
Nel mio caso, fortunatamente, si devono rappresentare al massimo tre cifre decimali e ci sono delle precise regole di arrotondamento.
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: bonmario - Dicembre 17, 2024, 07:32:56 am
Ok con TFloatSpinEdit si può prendere il value e fare i calcoli giusti però essendo un componente che rappresenta un numero "reale" sotto forma di stringa per essere visibile, se cambia (da solo) il decimalseparator lui mi farà vedere il numero con un altro decimal separator.

Potresti valutare anche un "TMaskEdit": gli dici tu direttamente come vuoi che venga scritto quel valore

https://wiki.freepascal.org/TMaskEdit

L'ho usato solo una volta, in un programma in cui l'utente doveva inserire una versione nel formato "XX.YY.ZZ_KKK", credo che possa andare bene anche nel tuo caso

Ciao, Mario
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: DragoRosso - Dicembre 17, 2024, 09:29:30 am
Infatti anch'io penso, a questo punto, che non ci siano alternative, però se il problema si manifesta, con lo stesso programma, solo su alcune macchine, allora vuol dire che una problematica c'è ed è propria di quelle macchine.
La problematica è legata ai settaggi del sistema operativo o a qualche componente che filtra i dati (può essere anche qualche funzione del SO stessa).

Fai attenzione, ciò che non è definito può sempre generare situazioni non previste. Se non imposti l'uso del "TFormatSetting" nelle funzioni di conversione che interessano le stringhe vuol dire che ti và bene quello che ti arriva:
l'esempio stupido è con i separatori di data e ora ... a seconda della "formazione" del PC questi possono essere  (anzi sono) diversi, anche se tutti sono settati con la lingua Italiana ... Win XP, Win 7, Win 8, Win 10, Win 11 ... aggiornamenti ....

Ciascuno di questi aveva impostazioni diverse e se un PC proviene come aggiornamento del SO da quelli vecchi chi può dire come sono settati ora ? Non parliamo dei PC con SO inglese o americano e il language pack Italiano che sembrano tutto e per tutto nativi Italiani, fatto salvo alcune impostazioni ....

Comunque, ciò che faccio io e non lasciare nulla al caso (almeno cerco). Non si trasmette nulla in stringa, se devo trasmettere a "qualcuno" con precisione di tre cifre allora preferisco trasmettere il (float *1000) arrotondato come intero. Esempio 100,002 -> 100002 e non ci sono problemi.

Se devo stampare e devo usare un formato preciso, quindi devo assolutamente impostare i TFormatSetting. Se devo scrivere in un file di "scambio" se non ci sono regole prestabilite è indifferente come scrivi la stringa ... se comunichi con un inglese si aspetta il 'punto', un francese la 'virgola', un vietnamita un carattere 'xxxx' (ho sparato a caso non prendete per buono ciò). Questo a livello di software, però con i francesi dobbiamo comunicare così come con gli altri e quindi dobbiamo necessariamente definire parametri o funzioni di filtro.

In JSON, devi definire la lingua usata (en-EN, it-IT per esempio) o le stringhe numeriche sono per definizione con il separatore decimale come PUNTO (corrisponde allo standard Americano che è il default).

Altri modi non ci sono.
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: nomorelogic - Dicembre 17, 2024, 10:03:14 am
Cose simili in passato sono successe anche a me.
Ricordo che ero giunto alla conclusione che gli Os Windows installati con setup inglese avevano questo problema, mentre quelli installati con setup italiano no.
Certo che alla fine cambia poco... FormatSettings come parametro credo sia l'unico modo.
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: tito_livio - Dicembre 17, 2024, 12:49:01 pm
Fai attenzione, ciò che non è definito può sempre generare situazioni non previste. Se non imposti l'uso del "TFormatSetting" nelle funzioni di conversione che interessano le stringhe vuol dire che ti và bene quello che ti arriva:
l'esempio stupido è con i separatori di data e ora ... a seconda della "formazione" del PC questi possono essere  (anzi sono) diversi, anche se tutti sono settati con la lingua Italiana ... Win XP, Win 7, Win 8, Win 10, Win 11 ... aggiornamenti ....
Un momento, forse mi sbaglierò ma io la vedo in questo modo:
Non è che mi va bene quello che arriva, io uso l'istruzione defaultsettings.decimalseparator:='.' e mi aspetto che dentro il programma il punto sia il separatore decimale, a prescindere da cosa ci sia fuori.
E infatti così funziona perfettamente nel 90% delle installazioni.
Se poi devo specificare lo stesso separatore decimale come parametro in ogni istruzione di conversione allora a cosa serve l'istruzione defaultsettings.decimalseparator:='.'?
Quindi secondo me c'è un bug in Lazarus o nel sistema operativo.
Che poi mi devo tenere il bug e modificare il codice, pazienza lo farò, ma vorrei prima scoprire il motivo di quella che credo sia una incopatibilità con qualcosa installato nel PC.

Con le date e gli orari, con formati diversi fra pc e pc, ci sono incappato anch'io, da un po' però non capita più.
In quel caso però ho cambiato i separatori di data e orario dentro Windows del cliente e si è risolto il problema.
Qui impostare il punto decimale dentro Windows mi sembra un po' troppo invasivo.

Potresti valutare anche un "TMaskEdit": gli dici tu direttamente come vuoi che venga scritto quel valore
Si, "TMaskEdit" lo uso per le date, per i numeri uso altri sistemi, ma qui il problema non è solo quello che inserisce l'utente, per esempio mentre il programma preleva i dati da db e li sta visualizzando ad un certo punto gli cambia (da solo) il separatore decimale di default e li visualizza con la virgola, i dati dell'utente invece rimangono col punto, insomma una bella confusione

Ricordo che ero giunto alla conclusione che gli Os Windows installati con setup inglese avevano questo problema, mentre quelli installati con setup italiano no.
Questo potrebbe essere una cosa da approfondire.
Farò un po' d'indagini e se scopro qualcosa ve lo dirò, per adesso grazie a tutti per le risposte.

Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: DragoRosso - Dicembre 17, 2024, 03:02:20 pm
Un momento, forse mi sbaglierò ma io la vedo in questo modo:
Non è che mi va bene quello che arriva, io uso l'istruzione defaultsettings.decimalseparator:='.' e mi aspetto che dentro il programma il punto sia il separatore decimale, a prescindere da cosa ci sia fuori.
E infatti così funziona perfettamente nel 90% delle installazioni.
Non conosco quella definizione (ossia "defaultsettings"), e bisogna vedere come è implementata nel codice FPC (o Lazarus). Essendo un parametro di default mi aspetto che possa comunque in qualche modo variare una volta modificata in risposta al cambiamento di ambiente o ad altre cause. Non mi fido a priori delle "cose" automatiche che non conosco.

Io uso una variabile globale DEL PROGRAMMA costruita con il TFormatSetting e modificata a mio piacimento per tutto, è la seconda cosa che "istanzio" dopo il mutex. E in ingresso, ove non sia possibile usare TFormatSetting come nella lettura dei file INI, filtro io il dato in modo da equipararlo sempre e comunque.

Per gli altri casi mi adeguo agli standard (vedi JSON).

E non è il problema del 10% o del 1% e neanche dello 0,001% ... se non mi funziona una installazione, anche solo una installazione in ... Cina .... si capisce che anche se perdo un giorno di tempo (ma anche tre) per adeguare tutte le mie funzioni allo standard ... ne vale ben la pena che un viaggio a mie spese in Cina.

Ripeto, non vedo altre soluzioni. Scorciatoie quante se ne vogliono ma soluzioni no.

Non volermene, non voglio essere aggressivo ma ti assicuro che ho esplorato queste situazioni anni fà (tanti anni fà) quando ho avuto a che fare con l'estero (e bada bene che con la Cina và anche ... prova con l'Arabo o il Giapponese e le sue varianti ....).

Certo, non con Lazarus ma con altri ambienti e la concretezza è identica.

Comunque se trovi la causa di tali situazioni postale che penso interessi a tutta la comnità.

Buona giornata e occhio a dove mettete i .... punti.

Ciaooo
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: quack - Dicembre 21, 2024, 12:19:26 pm
Infatti DefaultFormatSetting (non defaultsettings) è proprio una variabile di tipo TFormatSettings modificabile a runtime e utilizzata di default dalle funzioni FloatToStrF, TryStrToFloat ect... quando non viene passato il parametro FormatSettings. Riporto il codice di una delle funzioni citate:

Codice: [Seleziona]
Function TryStrToFloat(Const S : String; Out Value: Extended): Boolean;
begin
  Result:=TryStrToFloat(S,Value,DefaultFormatSettings);
end;

Function TryStrToFloat(Const S : String; Out Value: Extended; Const FormatSettings: TFormatSettings): Boolean;
Begin
  Result := TextToFloat(PChar(pointer(S)), Value,FormatSettings);
End;

Lo stesso vale per il componente TIniFile, di default i metodi ReadFloat e WriteFloat usano DefaultFormatSetting.

Quindi in modo diverso stiamo dicendo la stessa cosa.

Per quanto riguarda il componente TFloatSpindEdit, nel pacchetto LazControls c'è un'alternativa chiamata TFloatSpindEditEx che ha più opzioni, tipo la possibilità di usare il formato scientifico, un diverso separatore decimale, nascondere i pulsanti ect...

Una cosa che non mi piace di questa tipologia di componenti è che mostrano anche gli zeri non significativi dopo la virgola.
A titolo informativo, ho risolto con poche righe di codice creado un nuovo componente derivato dalla classe TFloatSpindEditEx con una funzione ValueToStr modificata che elimina gli zeri superflui.

Saluti
Titolo: Re:defaultsettings.decimalseparator cambia da solo
Inserito da: tito_livio - Dicembre 24, 2024, 03:36:46 pm
Per quanto riguarda il componente TFloatSpindEdit, nel pacchetto LazControls c'è un'alternativa chiamata TFloatSpindEditEx che ha più opzioni, tipo la possibilità di usare il formato scientifico, un diverso separatore decimale, nascondere i pulsanti ect...

Una cosa che non mi piace di questa tipologia di componenti è che mostrano anche gli zeri non significativi dopo la virgola.
A titolo informativo, ho risolto con poche righe di codice creado un nuovo componente derivato dalla classe TFloatSpindEditEx con una funzione ValueToStr modificata che elimina gli zeri superflui.

Ciao,
interessante, il fatto che questo componente lasci gli zeri non significativi e nasconda i pulsanti, per me potrebbe essere utile per visualizzare valuta o risultati di laboratorio.