2017-03-10 1 views
2

Ich verwende C++ Builder XE4 mit der Windows 32-Bit VCL-Plattform.Funktionieren meine US-Ini-Dateien mit Gleitkommazahlen im europäischen Format

Ich richte .INI Dateien ein, die Fließkommazahlen enthalten, die einen Punkt (.) als Dezimaltrennzeichen verwenden. In Europa verwenden sie das Komma (,) als Dezimaltrennzeichen.

Ich verwende die Klasse TIniFile, die in C++ Builder integriert ist.

Will TIniFile lesen Sie die Dateieinträge unten in Europa? Brauche ich eine zweite .INI Datei mit dem Komma als Trennzeichen?

Wenn ich die Float-Werte lese, verwendet mein fehlersicherer Rückgabewert auch den Punkt (.). Im folgenden Code

ReadFloat("Forex", "Size", 0.01 ); 

0,01 ist der ausfallsichere Rückgabewert.

Wird das in Europa funktionieren?

Zum Beispiel:

MyList.ini:

[Forex] 
Size=0.0001 
Value=10.5 
OffSet=0.01 


//Read TIniFile 
MyList = new TIniFile("C:\\MyList.ini"); 
double r_Size = MyList->ReadFloat("Forex", "Size", 0.01); 
double r_Value = MyList->ReadFloat("Forex", "Value", 10.0); 
double r_OffSet = MyList->ReadFloat("Forex", "OffSet", 0.01); 

Bearbeiten Unten finden

Sie in Ihrer Antwort angezeigt, wenn die TIniFile geschrieben wurde an der gleichen Stelle und lesen Sie es richtig gelesen würde. Wäre es möglich, meine .INI-Dateien einmal zu öffnen und erneut zu speichern, um sie in das lokale Format zu konvertieren? Dann könnte ich meinen aktuellen Code ohne Änderung verwenden. Ich würde die folgenden drei Schritte in einer Schleife für jeden Float in meinen INI-Dateien ausführen. Würde das funktionieren, um die INI-Dateien neu zu formatieren?

Ich müsste nur den fehlersicheren Rückgabewert anpassen, um das lokale Trennzeichen zu verwenden.

Schritt 1) ​​überprüfen Sie lokalen Wert von Separator
Schritt 2) Last Float US-Format (.) Separator
Schritt 3) Re-Speicher mit lokalem Separator

void __fastcall ConvertToLocalSeparator(){ 

//1 Check Local Value of Seperator 
TFormatSettings fmt = TFormatSettings::Create(); 
UnicodeString Local = fmt.DecimalSeparator; 
UnicodeString Euro = ","; 

if(CompareText(Local, Euro)==0){ //Local DecimalSeparator is Coma(,) 

//2 Load Float using US format (.) Separator 
TFormatSettings USfmt = TFormatSettings::Create(); // get defaults 
USfmt.DecimalSeparator = '.'; 
USfmt.ThousandSeparator = '\0'; //'\0' to disable 

TIniFile *MyList; 
MyList = new TIniFile("C:\\MyList.ini"); 
double r_Size = StrToFloatDef(MyList->ReadString("Forex", "Size", ""), 0.01, USfmt); 
delete MyList; 
MyList=NULL; 

//3 Re-Save with local Separator 
MyList = new TIniFile("C:\\MyList.ini"); 
MyList->WriteFloat("Forex", "Size", r_Size ); 
delete MyList; 
MyList=NULL; 
} 

} 

Antwort

7

Intern ReadFloat() verwendet ReadString() und ruft dann die NON-TFormatSettings-Version StrToFloat() auf, um die String in eine double umzuwandeln. Ebenso ruft WriteFloat() die NON-TFormatSettings -Version FloatToStr() auf, konvertiert die double in eine String und ruft dann WriteString() auf.

Diese Versionen von StrToFloat() und FloatToStr() ist abhängig von dem globalen DecimalSeparator Variable in der SysUtils Einheit, die locale-abhängig ist. So NO, Ihr vorhandener Code wird NICHT arbeiten als ist, wenn ReadFloat() auf einem System aufgerufen wird, das ein anderes Gebietsschema als das System verwendet, das WriteFloat() aufruft.

Um dies zu umgehen, haben Sie zwei Möglichkeiten:

  1. Änderung der Wert der globalen DecimalSeparator Variable '.'. Nicht empfohlen, kann aber gemacht werden.

  2. vergessen WriteFloat() und ReadFloat() insgesamt. Verwenden Sie direkt WriteString() und ReadString(), und führen Sie Float-Konvertierungen selbst durch, damit Sie die von Ihnen gewünschte feste Formatierung auf allen Systemen konsistent verwenden können.

    //Write TIniFile 
    
    TFormatSettings fmt = TFormatSettings::Create(); // get defaults 
    fmt.DecimalSeparator = _D('.'); 
    fmt.ThousandSeparator = _D(','); // or '\0' to disable 
    
    MyList = new TIniFile(_D("C:\\MyList.ini")); 
    MyList->WriteString(_D("Forex"), _D("Size"), FloatToStr(r_Size, fmt)); 
    MyList->WriteString(_D("Forex"), _D("Value"), FloatToStr(r_Value, fmt)); 
    MyList->WriteString(_D("Forex"), _D("OffSet"), FloatToStr(r_OffSet, fmt)); 
    

    //Read TIniFile 
    
    TFormatSettings fmt = TFormatSettings::Create(); 
    fmt.DecimalSeparator = _D('.'); 
    fmt.ThousandSeparator = _D(','); // or '\0' to disable 
    
    MyList = new TIniFile(_D("C:\\MyList.ini")); 
    double r_Size = StrToFloatDef(MyList->ReadString(_D("Forex"), _D("Size"), _D("")), 0.01, fmt); 
    double r_Value = StrToFloatDef(MyList->ReadString(_D("Forex"), _D("Value"), _D("")), 10.0, fmt); 
    double r_OffSet = StrToFloatDef(MyList->ReadString(_D("Forex"), _D("OffSet"), _D("")), 0.01, fmt); 
    

aktualisieren: Wenn Sie die Datei mit dem lokalen Format (was ich nicht empfehlen) lesen und erneut speichern wollen, Sie könnten etwas mehr wie folgt versuchen:

TFormatSettings USFmt; 
TFormatSettings EuroFmt; 

void __fastcall InitFormats() 
{ 
    USFmt = TFormatSettings::Create(); 
    USFmt.DecimalSeparator = _D('.'); 
    USFmt.ThousandSeparator = _D('\0'); 

    EuroFmt = TFormatSettings::Create(); 
    EuroFmt.DecimalSeparator = _D(','); 
    EuroFmt.ThousandSeparator = _D('\0'); 
} 

void __fastcall CheckLocalFormat(TCustomIniFile *Ini, String Section, String Name, double Default) 
{ 
    double value; 
    String s = Ini->ReadString(Section, Name, _D("")); 
    if (!TryStrToFloat(s, value)) 
    { 
     if (!TryStrToFloat(s, value, USFmt) && !TryStrToFloat(s, value, EuroFmt)) 
      value = Default; 
     Ini->WriteFloat(Section, Name, value); 
    } 
} 

void __fastcall CheckLocalFormat() 
{ 
    TIniFile *MyList = new TIniFile(_D("C:\\MyList.ini")); 
    CheckLocalFormat(MyList, _D("Forex"), _D("Size"), 0.01); 
    CheckLocalFormat(MyList, _D("Forex"), _D("Value"), 10.0); 
    CheckLocalFormat(MyList, _D("Forex"), _D("Offset"), 0.01); 
    delete MyList; 
} 
+0

Natürlich funktioniert das Schreiben und Lesen mit 'TFormatSettings :: Invariant' auch. Es wird überall gleich sein (mehr oder weniger dasselbe wie die US-Formateinstellungen, AFAIK). Dann müssen die Dezimaltrennzeichen und Tausendertrennzeichen nicht festgelegt werden. Ich bin mir nicht sicher, ob das schon in XE4 verfügbar ist, aber ich denke schon. Andernfalls kann man es mit dem TFormatSettings-Konstruktor bekommen. –

+0

Ich habe diese Lösung sowohl auf der TMemIniFile als auch auf der TIniFile getestet und scheint mit beiden korrekt zu funktionieren. Danke – homebase

+0

Remy, kannst du erklären, warum du das _D in deinen Beispielen benutzt hast? Ich habe im Embarcadero-Wiki, das sich mit TStringBuilder beschäftigt, nur begrenzte Dokumentation gefunden und verstehe nicht wirklich, was es tut. _D scheint nur mit Text in Anführungszeichen "" verwendet zu werden. – homebase

Verwandte Themen