Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   inifile ReadFloat deutsch/english formatsettings (https://www.delphipraxis.net/208319-inifile-readfloat-deutsch-english-formatsettings.html)

bernhard_LA 14. Jul 2021 11:03

inifile ReadFloat deutsch/english formatsettings
 
Würde gerne mit einem Deutschen Windows PC ( dezimale = 1,2 ) Ini files einlesen die jemand auf einem englischen PC geschrieben hat ( 1.2)
mit der Funktion Ini.ReadFloat(....) .
Was muss ich machen damit ich beide Formate einlesen kann ?


Delphi-Quellcode:
fIni := TIniFile.Create(aFileName);
  try

    X := fIni.ReadFloat(SectionStr, 'x', -99999); //  mag nur Komma-Zahlen mit Deutschen Format 1,2   :-( 


  finally
    fIni.free;
  end;

bcvs 14. Jul 2021 11:11

AW: inifile ReadFloat deutsch/english formatsettings
 
Als String einlesen und mit StrToFloat umwandeln. Dabei die FormatSettings nach Bedarf setzen.

bernhard_LA 14. Jul 2021 11:24

AW: inifile ReadFloat deutsch/english formatsettings
 
gibt's ne Lösung bei der ich nix selber schreiben muss .... eigentlich doch ne Aufgabe der Klasse inifile ......

himitsu 14. Jul 2021 11:37

AW: inifile ReadFloat deutsch/english formatsettings
 
Erstmal ist es totaler Schwachsinn, dass INIs überhaupt lokalisiert gespeichert/gelesen werden.
(aber für INIs gibt es ja leider auch eigentlich keine globale einheitliche Konvention/Richtline/Norm/...)
Und leider kam in den letzten 20 Jahren auch noch keiner auf die Idee eine FormatSettings-Variable in TCustomIniFile für Floats und DateTimes einzufügen.

Fazit:
  • Du mußt, wie schon gesagt wurde, selbst den Float behandeln
    Delphi-Quellcode:
    StrToFloat(INI.ReadString(...), DeinFormatSettings)
    • manuell, vor Ort
    • TIniFile ableiten und diese Funktionen überschreiben überdecken
    • oder besser noch einfach mit einem ClassHelper erweitern, z.B. um ein ReadFloatUS
  • oder du stellst das globale FormatSettings in der SysUtils um
    • also entweder vor und nach dem Zugriff auf die INI, vorübergehend (aber ist auch keine gute Idee, da nicht threadsave usw.)
    • oder permanent dein Programm auf Englisch umstellen
      Delphi-Quellcode:
      System.SysUtils.FormatSettings := TFormatSettings.Create('en-US');

Aber vielleicht wird das Problem ja im nächsten Jahrtausend behoben sein. :stupid:
https://quality.embarcadero.com/browse/RSP-17616

Der schöne Günther 14. Jul 2021 11:49

AW: inifile ReadFloat deutsch/english formatsettings
 
Oder du machst es dir einmal als
Delphi-Quellcode:
class helper
und benutzt dann einfach die folgende Unit in deiner
Delphi-Quellcode:
uses
. Das ist das schwere Los eines Software-Entwicklers, manchmal muss man doch tatsächlich wenigstens noch ein
Delphi-Quellcode:
uses
tippen.


Delphi-Quellcode:
unit System.IniFiles.TCustomIniFile.Helper;

interface uses System.IniFiles, System.SysUtils;

type
   /// <summary>
   ///     Ersetzt die Methode
   ///    <c>WriteFloat</c> und deren <i>Read</i>-Gegenstück da die
   ///    RTL-Implementation von <c>TCustomIniFile</c> dummerweise
   ///    mit den <b>globalen Formatsettings</b> arbeitet
   /// </summary>
   /// <remarks>
   ///     Floats werden mit <c>.</c> als <c>DecimalSeparator</c> geschrieben.
   /// </remarks>
   TCustomIniFileHelper = class helper for TCustomIniFile
      protected class var
         internalFormatSettings: TFormatSettings;
      protected
         class constructor Create();
      public
         procedure WriteFloat(const Section, Name: string; Value: Double);
         function ReadFloat(const Section, Name: string; Default: Double): Double;
   end;


implementation

{ TCustomIniFileHelper }

class constructor TCustomIniFileHelper.Create();
begin
   internalFormatSettings := TFormatSettings.Invariant();
end;

function TCustomIniFileHelper.ReadFloat(
   const Section, Name: string;
   Default: Double
): Double;
var
   FloatStr: string;
begin
   FloatStr := ReadString(Section, Name, '');
   Result := Default;
   if FloatStr <> '' then
   try
      Result := StrToFloat(FloatStr, internalFormatSettings);
   except
      on EConvertError do
         Result := inherited ReadFloat(Section, Name, Default);
   else
      raise;
   end;
end;

procedure TCustomIniFileHelper.WriteFloat(
   const Section, Name: string;
   Value: Double
);
begin
   WriteString(Section, Name, FloatToStr(Value, internalFormatSettings));
end;

end.

Uwe Raabe 14. Jul 2021 13:24

AW: inifile ReadFloat deutsch/english formatsettings
 
Zitat:

Zitat von bernhard_LA (Beitrag 1492271)
gibt's ne Lösung bei der ich nix selber schreiben muss .... eigentlich doch ne Aufgabe der Klasse inifile ......

Das sehen andere zwar auch so (https://quality.embarcadero.com/browse/RSP-17616), aber solange das nicht implementiert ist musst du wohl selber ran.

Rolf Frei 14. Jul 2021 15:30

AW: inifile ReadFloat deutsch/english formatsettings
 
Doppelpost

Rolf Frei 14. Jul 2021 15:53

AW: inifile ReadFloat deutsch/english formatsettings
 
Eigentlich reicht es doch einfach das DecimalChar zu setzen:
Delphi-Quellcode:
var OrgDecSep: Char;
fIni := TIniFile.Create(aFileName);
  try
    try
      X := fIni.ReadFloat(SectionStr, 'x', -99999); // mag nur Komma-Zahlen mit Deutschen Format 1,2 :-(
    except
      OrgDecSep := FormatSettings.DecimalSeparator;
      try    
        FormatSettings.DecimalSeparator := '.';
        X := fIni.ReadFloat(SectionStr, 'x', -99999); // mag nur Komma-Zahlen mit Deutschen Format 1,2 :-(
      finally
        FormatSettings.DecimalSeparator := OrgDecSep;
      end;
    end;
  finally
    fIni.free;
  end;

Oder wenn du genau weisst, dass das INI nach US/ANSI Regel gespeichert wurde, kannst du das auch einmalig vor der Verarbeitung der INI machen:
Delphi-Quellcode:
var OrgDecSep: Char;
OrgDecSep := FormatSettings.DecimalSeparator;
fIni := TIniFile.Create(aFileName);
  try
    FormatSettings.DecimalSeparator := '.';
    X := fIni.ReadFloat(SectionStr, 'x', -99999); // mag nur Komma-Zahlen mit Deutschen Format 1,2 :-(
    ... weitere ReadFLoat, etc.
  finally
    FormatSettings.DecimalSeparator := OrgDecSep;
    fIni.free;
  end;

Uwe Raabe 14. Jul 2021 15:54

AW: inifile ReadFloat deutsch/english formatsettings
 
Zitat:

Zitat von Rolf Frei (Beitrag 1492287)
Eigentlich reicht es doch einfach den DecimalSeparator zu setzen

Wenn sich das ganze nicht in einem Thread abspielt, geht das wohl.

himitsu 14. Jul 2021 16:29

AW: inifile ReadFloat deutsch/english formatsettings
 
Es ist egal, ob DAS in einem Thread oder im Hauptthread abläuft ... wenn irgendwer Anderes in einem "anderen" Thread gleichzeitig das auch nutzen oder gar ebenfalls ändern will, dann ...

dummzeuch 14. Jul 2021 17:47

AW: inifile ReadFloat deutsch/english formatsettings
 
Da mich das auch immer genervt hat, habe ich vor Ewigkeiten die TIniFile_TryReadFloat und TIniFile_ReadFloat Hilfsfunktionen geschrieben, die dieses Problem lösen. Zusammen mit TIniFile_WriteFloat, welche immer mit Dezimalpunkt schreibt, sind diese Funktionen Teil der Unit u_dzClassUtils meiner dzlib.

Redeemer 14. Jul 2021 20:46

AW: inifile ReadFloat deutsch/english formatsettings
 
Ich habe meine Anwendung so umgestellt, dass der Speicher als Int64 interpretiert wird, wenn ich ein TDateTime speichern oder lesen möchte.

Delphi-Quellcode:
type TJanniINIFile = class helper for TIniFile
  public
    constructor CreateForceFile(const FileName: string); // Erstellt gegebenenfalls den Ordner
    procedure WriteDouble(const Section, Ident: string; const Value: Double); // Mein Versuch, TDateTime regionsunabhängig zu schreiben
    function ReadDouble(const Section, Ident: string; const Default: Double): Double;
end;

{ TJanniINIFile }

constructor TJanniINIFile.CreateForceFile(const FileName: string);
begin
  ForceDirectories(ExtractFilePath(Filename));
  Create(FileName);
end;

function TJanniINIFile.ReadDouble(const Section, Ident: string;
  const Default: Double): Double;
var
  Pointer: PDouble;
  Pointer2: PInt64;
  Int: Int64;
begin
  Pointer2 := @Default;
  Int := StrToInt64Def(ReadString(Section, Ident, ''), Pointer2^);
  Pointer := @Int;
  Result := Pointer^;
end;

procedure TJanniINIFile.WriteDouble(const Section, Ident: string;
  const Value: Double);
var
  Pointer: PInt64;
begin
  Pointer := @Value;
  WriteString(Section, Ident, IntToStr(Pointer^));
end;

freimatz 15. Jul 2021 10:50

AW: inifile ReadFloat deutsch/english formatsettings
 
Pointer sind bei uns - zum Glück - zu Recht - verboten.
Wenn es um Datum geht würde ich das in ANSI-Format speichern. Aber Fragenden ging es wohl um "normale" Double.

Zitat:

Zitat von himitsu (Beitrag 1492272)
Erstmal ist es totaler Schwachsinn, dass INIs überhaupt lokalisiert gespeichert/gelesen werden.
(aber für INIs gibt es ja leider auch eigentlich keine globale einheitliche Konvention/Richtline/Norm/...)

Ist das bei XML und/oder JSON anders?

Rolf Frei 15. Jul 2021 11:20

AW: inifile ReadFloat deutsch/english formatsettings
 
Ja. XML kann über einen Header bestimmt werden, was für eine Codierung gilt. Ohne den Header ist es per Definition UTF-8. JSON ist nach meinem Wissenstand per Definition immer UTF-8.

Redeemer 15. Jul 2021 11:29

AW: inifile ReadFloat deutsch/english formatsettings
 
Zitat:

Zitat von Rolf Frei (Beitrag 1492338)
Ja. XML kann über einen Header bestimmt werden, was für eine Codierung gilt. Ohne den Header ist es per Definition UTF-8. JSON ist nach meinem Wissenstand per Definition immer UTF-8.

Um Zeichenkodierung geht es hier nicht.

jziersch 15. Jul 2021 11:55

AW: inifile ReadFloat deutsch/english formatsettings
 
Hier als Lösungsvorschlag eine Funktion ConvertStr um einen String in ein Double zu verwandeln
und dabei . und , alternativ zu akzeptieren sowie spaces zu ignorieren.
Um INI Dateien zu lesen meiner Ansicht nach recht zweckmässig, um beim Start eines Programmes Exceptions zu vermeiden.


Code:
function ConvertStr( const str : string ) : Double;
var i : Integer;
    fNeg, fDez : Boolean;
    c : Char;
    fN, fDD : Double;
begin
      fN := 0;
      fNeg := FALSE;
      fDez := FALSE;
      fDD := 10;
      for i := 1 to Length(str) do
      begin
        c := str[i];
        if c = '-' then fNeg := not fNeg
        else if c = '.' then fDez := true
        else if c = ',' then fDez := true
        else if c in ['0'..'9'] then
        begin
          if fDez then
          begin
            fN := fN + (Integer(c) - Integer('0'))/fDD;
            fDD := fDD * 10;
          end else
          begin
            fN := fN * 10 + (Integer(c) - Integer('0'));
          end;
        end
        // Wenn auskommentiert werden unerwartete Zeichen einfach ignoriert
        // else break
        ;
      end;
      if fNeg then Result := -fN
      else Result := fN;
end;

himitsu 15. Jul 2021 12:13

AW: inifile ReadFloat deutsch/english formatsettings
 
XML nein, da sind Floats garnicht gefiniert (alles als String und das Format ist nicht vorgegeben)

aber JSON ja, denn dort ist ALLES ganz genau definiert, zumindestens bei Sting, Boolean, Integer und Float. (fast, denn beim Datum gibt es zwar indirekt eine Quasi-Vorgabe, aber Datum als "Typ" gibt es das per-se nicht)
https://www.json.org/json-de.html

Rolf Frei 15. Jul 2021 15:45

AW: inifile ReadFloat deutsch/english formatsettings
 
Zitat:

Zitat von Redeemer (Beitrag 1492341)
Zitat:

Zitat von Rolf Frei (Beitrag 1492338)
Ja. XML kann über einen Header bestimmt werden, was für eine Codierung gilt. Ohne den Header ist es per Definition UTF-8. JSON ist nach meinem Wissenstand per Definition immer UTF-8.

Um Zeichenkodierung geht es hier nicht.

Ja hast natürlich recht. Hatte gedacht weil es um definierte Vorgaben geht. Wenn man aber Doubles,Floats, etc. speichert sollten diese der ANSI Regeln für die Zahlandrestellung nutzen oder multipliziren mit 100 oder sawas. Bei allen XML die ich bisher verabreiten musste, wurde vom Interface (Producer des XML) vorgegeben, dass es mit 100 Multipliziert werden muss (im Fall von Geldbeträgen) oder aber der Punkt als Pflicht vorgegeben ist. Bei Datumsangaben ist es übrignesn das selbe. Die Daten in lokalen Format zu speichern ist aber auf jeden Fall unzulässig und falsch.

freimatz 15. Jul 2021 16:43

AW: inifile ReadFloat deutsch/english formatsettings
 
Und was sind die ANSI-Regeln für Zahlendarstellung?
Wen ich google nach "ansi Zahlendarstellung" komme ich z.B. auf https://de.wikipedia.org/wiki/IEEE_754

himitsu 15. Jul 2021 17:37

AW: inifile ReadFloat deutsch/english formatsettings
 
IEEE754 ist für das interne Speicherformat innerhalb des Speichers.

Aber passend zu diesem Format gibt es.
https://www.oreilly.com/library/view...2521/re67.html

Redeemer 16. Jul 2021 07:49

AW: inifile ReadFloat deutsch/english formatsettings
 
Zitat:

Zitat von freimatz (Beitrag 1492334)
Wenn es um Datum geht würde ich das in ANSI-Format speichern. Aber Fragenden ging es wohl um "normale" Double.

Es gibt keinen Unterschied zwischen TDateTime und Double. Es sind einfach zwei Begriffe für exakt dasselbe, nämlich IEEE-754-Double. Nur die Darstellung von generischen Double-Werten als Datum ist nicht so toll, da u.U. ungültig (max 9999?).

Zitat:

Zitat von freimatz (Beitrag 1492334)
Pointer sind bei uns - zum Glück - zu Recht - verboten.

Und mit Windows kommuniziert ihr in Morsecode?
Es gibt halt keinen verlustfreien Weg, eigentlich inkompatiblen Speicher zu casten.

Rolf Frei 16. Jul 2021 11:55

AW: inifile ReadFloat deutsch/english formatsettings
 
Zitat:

Zitat von freimatz (Beitrag 1492367)
Und was sind die ANSI-Regeln für Zahlendarstellung?
Wen ich google nach "ansi Zahlendarstellung" komme ich z.B. auf https://de.wikipedia.org/wiki/IEEE_754

Finde gerade auch nichts Vernünfitges, aber es entsprcht eigentlich grösstenteils dem US Format, also wie folgt:
DezimalSeparator = .
ThousandSeparator = ,

Der ThousandSeparator würde ich aber beim Speichern der Zahl immer weglassen und nur den Punkt als Dezimalzeichen verwenden. Beim Einlsende der Daten muss einfach klar sein, wie diese Zahlen vorliegen und das dann entsprechend behandelt werden. Ich mache das immer nach mienem Beispiel, das ich gepostet habe. Thread spielen bei mir dabei keine Rolle, da ich sowas nicht in einem Thread nutze. Da müsste man das dann anders lösen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:51 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz