Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Prism Fließkommazahlen Delphi vs. Oracle (https://www.delphipraxis.net/93310-fliesskommazahlen-delphi-vs-oracle.html)

Raffigator 4. Jun 2007 10:42

Datenbank: Oracle • Version: 10g • Zugriff über: Bdp

Fließkommazahlen Delphi vs. Oracle
 
Hallo,
ich habe ein Problem mit dem Decimalseparator.
Ich möchte über eine Delphi-Anwendung Fließkommazahlen in einer Oracle-Datenbank speichern. Oracle erwartet einen '.' als Trennzeichen, während Delphi das ',' benutzt. Ich habe die NLS_NUMERIC_CHARACTERS umgestellt, so dass ein Speichern möglich ist. Die Zahlen werden auch richtig in der Datenbank abgelegt!
Lese ich nun aber mit meiner Anwendung die Tabelle wieder aus, wird das Trennzeichen einfach überlesen und ich habe anstatt 7.45 nun 745.
Kann mir jemand einen Tip geben, wie man am besten mit Fließkommazahlen umgeht?

Der_Unwissende 4. Jun 2007 10:57

Re: Fließkommazahlen Delphi vs. Oracle
 
Zitat:

Zitat von Raffigator
Ich möchte über eine Delphi-Anwendung Fließkommazahlen in einer Oracle-Datenbank speichern. Oracle erwartet einen '.' als Trennzeichen, während Delphi das ',' benutzt.

Hi,
so ganz richtig ist das nicht. Eigentlich zeigt das aus schon, dass Du auf den National Language Support zurückgreifst. An sich benutzen sowohl Delphi als auch Oracle einfach lokalisierte Einstellungen. Delphi greift dabei immer auf die Einstellungen unter Windows zurück. I.d.R. wirst Du in Deutschland Rechner mit deutscher Lokalisierung finden und hier ist eben das Komma der Dezimalseperator. Wichtig ist, dass Du Dich eigentlich nie auf diesen verlassen solltest. Das man selbst einen solchen Rechner verwendet heißt natürlich nicht, dass alle Rechner auf denen das Programm einmal läuft ebenfalls diesen Dezimalseperator verwenden. So kann ein Kunde durchaus einen englisch lokalisierten haben, der erwartet dann den Dezimalpunkt (und natürlich kommen noch ganz andere Dinge in Frage).
Deshalb greift man häufig auf eine einheitliche Lokalisierung zurück, damit eine Datei auf einem dt. Rechner gespeichert und problemfrei auf einem eng. geladen werden kann und vice versa.

Dafür gibt es spezielle Strukturen, die Informationen über die bei einer Konvertierung zu verwendenden Einstellungen enthalten. Unter Delphi.Win32 währen das TFormatSettings, unter .net findest Du Implementierungen des IFormatProvider-Interface. In Deinem Fall die Klasse NumberFormatInfo und hier die Eigenschaft numberDecimalSeperator. Erzeuge hier eine neue Instanz, setz den Dezimalseperator und übergib diese Instanz dann an die toString Methode als zweites Argument (erstes ist weiterhin die zu konvertierende Zahl).

Gruß Der Unwissende

Raffigator 4. Jun 2007 11:19

Re: Fließkommazahlen Delphi vs. Oracle
 
Hi,

danke erstmal für deine Antwort!
Problem ist, ich benutze keine toString-Methode. Ich lese die Datenbank aus und fülle den Inhalt in ein Datagrid bzw. DataSet und dort verschwindet das Komma dann einfach.
Beim Speichern habe ich das Problem, dass wenn ich die NLS-Einstellungen nicht vornehme jedesmal ein Fehler auftritt, wenn ich meinen Datenkomponenten einen Floatwert zuweise

Delphi-Quellcode:
     
with BdpUpdate2, Datensatz do begin
        if Connection.State = System.Data.ConnectionState.Closed then
          Connection.Open;

        Parameters.Item[0].Value := fConvertStrToDouble(VS,2);
        Parameters.Item[1].Value := U1ERST1;
        if U1SATZ1 <> '' then
          Parameters.Item[2].Value := fConvertStrToDouble(U1SATZ1,2);
        Parameters.Item[3].Value := U1ERST2;
        if U1SATZ2 <> '' then
          Parameters.Item[4].Value := fConvertStrToDouble(U1SATZ2,2);
        Parameters.Item[5].Value := U1ERST3;
        if U1SATZ3 <> '' then
          Parameters.Item[6].Value := fConvertStrToDouble(U1SATZ3,2);
        Parameters.Item[7].Value := U1ERST4;
        if U1SATZ4 <> '' then
          Parameters.Item[8].Value := fConvertStrToDouble(U1SATZ4,2);
        Parameters.Item[9].Value := U1ERST5;
...
end;
Wenn ich den Parametern beispielsweise einfach einen String zuweise wie 7.45, '7.45', oder aber auch 7,45, wird das nicht akzeptiert, da es nicht als Floatwert erkannt wird...

Der_Unwissende 4. Jun 2007 11:38

Re: Fließkommazahlen Delphi vs. Oracle
 
Zitat:

Zitat von Raffigator
Problem ist, ich benutze keine toString-Methode.

Ok, Du verwendest die fConvertStrToDouble-Methode. Da ich gerade nichts zu der finde mal die Frage, wo kommt die denn her? Was macht die denn? Und falls die nur in meiner Hilfe fehlt, gibt es die überladen, so dass man hier wieder einen Dezimalseperator angeben könnte?

Zitat:

Zitat von Raffigator
Beim Speichern habe ich das Problem, dass wenn ich die NLS-Einstellungen nicht vornehme jedesmal ein Fehler auftritt, wenn ich meinen Datenkomponenten einen Floatwert zuweise

Das ist klar. Das kannst Du halt wieder ändern, sobald Delphi automatisch den korrekten Dezimalseperator verwendet.

Raffigator 4. Jun 2007 11:48

Re: Fließkommazahlen Delphi vs. Oracle
 
Zitat:

Zitat von Der_Unwissende
Ok, Du verwendest die fConvertStrToDouble-Methode. Da ich gerade nichts zu der finde mal die Frage, wo kommt die denn her? Was macht die denn? Und falls die nur in meiner Hilfe fehlt, gibt es die überladen, so dass man hier wieder einen Dezimalseperator angeben könnte?

Nein, die gibt es nicht überladen, da ich mir die Methode selber geschrieben habe. Ich paste sie mal hier rein

Delphi-Quellcode:
{_______________________________________________________________________________
Wandelt einen String in eine Floatzahl um

   @param Fstring (in)   : String, der umgewandelt werden soll
   @param decimals (in)  : Anzahl der Nachkommastellen

   @return (out)         : 0     --> Zahl konnte nicht umgewandelt werden
                            sonst --> umgewandelte Float-Zahl
_______________________________________________________________________________}
function fConvertStrToDouble(Fstring : string ; decimals : integer):double;
var
  i : byte;
  temp : double;
  settings : TFormatSettings;
begin
  Result := 0;
  kommaCount := 0;
 
 //englischen Zahlen-Standard laden (Decimalseperator = '.')
  GetLocaleFormatSettings(1033, settings);
  for i := 1 to Length(Fstring) do
    if Fstring[i] = ',' then begin
       fString[i] := '.';
    end;

 
  //String umwandeln
  if TryStrToFloat(Fstring,temp,settings) then
    Result := temp;
end;
Beschreibe nochmal kurz, was ich überhaupt machen will. Also ich lese eine Txt-Datei ein, in der Datensätze gespeichert sind. Es geht halt darum diese Datensätze nun in der Datenbank zu speichern. In der Txt-Datei sind die Fließkommazahlen mit einem ',' gespeichert.
Ich lese also die Zahl mit dem Komma als String ein und will es dann in der fConvertStrToDouble umwandeln. Wie du siehst habe ich da auch schonmal mit den TFormatSettings gearbeitet. (Ist das überhaupt .Net-Kompatibel?)
Wie gesagt, bisher sind bei mir alle Versuche mit dem Komma als Separator zu arbeiten, fehlgeschlagen.

Elvis 4. Jun 2007 12:04

Re: Fließkommazahlen Delphi vs. Oracle
 
Warum musst du überhaupt umwandeln? DAS ist der große Fehler. ;-)
Dem Oracle client ist es schnurz-piepe was die Regionaleinstellungen des Users im Betriebssystems sind.
Steht in der Registry AMERICA_AMERICAN, dann wird er das nehmen.
Du hast also in deiner Anwendung keine große Möglichkeit anhand der Windowseinstellungen auf die Oracle einstellungen zu schließen. [1]

Aber jetzt kommt der Punkt: Warum auch?
Eine Number(10,4) kann in in einer DataColumn mit ähnlicher Formatierung abgebildet werden.
SQL Statements sollten natürlich immer mit vernünftig typisierten Parametern erfolgen.
Somit sollte es für dich niemals das Problem geben, dass du in deinem Code einen Dezimalwert als String benutzen willst.
Die einzige Stelle wo das passiert ist in dem Control an das die Daten gebunden wurden.
Stört dich aber im Code nicht weiter, weil es jede Eingabe verhindert, die nicht in den Zieltypen umgewandelt werden kann.

Wie du bereits gemerkt hast, stellt das Datagrid nur die Dinge da, die du ihm als Datenquelle vorgibst, gib ihm einfach die richtigen Spaltentypen und alles ist in Butter...

[1] Nicht lange darüber wundern: Oracle hat neben extrem komplexen und ähm coolen Features, soviele mittelalterliche und hirnrissige Macken und/oder Implementierungslücken, es würde Monate dauern die alle aufzulisten... :?

Der_Unwissende 4. Jun 2007 12:04

Re: Fließkommazahlen Delphi vs. Oracle
 
Zitat:

Zitat von Raffigator
In der Txt-Datei sind die Fließkommazahlen mit einem ',' gespeichert.
Ich lese also die Zahl mit dem Komma als String ein und will es dann in der fConvertStrToDouble umwandeln. Wie du siehst habe ich da auch schonmal mit den TFormatSettings gearbeitet. (Ist das überhaupt .Net-Kompatibel?)
Wie gesagt, bisher sind bei mir alle Versuche mit dem Komma als Separator zu arbeiten, fehlgeschlagen.

Ok, nochmal, Du liest Die Daten aus einer txt-Datei? Wenn diese dort mit einem Komma als Seperator stehen, solltest Du auf jeden Fall die Umwandlung immer mit einem Komma als Dezimalseperator vornehmen. Ich dachte eigentlich, dass Du die Werte eben als Strings in eine SQL-Anfrage steckst und Oracle dann damit seine Probleme hat. Die Übergabe von umgewandelten Zahlen sollte hingegen kein Problem sein, da sich hier die Anbindung der DB um die korrekte Umwandlung kümmern müsste. Wenn Du allerdings dort Deinen Fehler hast (hab ich dann nur falsch verstanden), dann fällt mir auch nichts ein.

Elvis 4. Jun 2007 12:12

Re: Fließkommazahlen Delphi vs. Oracle
 
Zitat:

Zitat von Der_Unwissende
Ok, nochmal, Du liest Die Daten aus einer txt-Datei? Wenn diese dort mit einem Komma als Seperator stehen, solltest Du auf jeden Fall die Umwandlung immer mit einem Komma als Dezimalseperator vornehmen. Ich dachte eigentlich, dass Du die Werte eben als Strings in eine SQL-Anfrage steckst und Oracle dann damit seine Probleme hat. Die Übergabe von umgewandelten Zahlen sollte hingegen kein Problem sein, da sich hier die Anbindung der DB um die korrekte Umwandlung kümmern müsste. Wenn Du allerdings dort Deinen Fehler hast (hab ich dann nur falsch verstanden), dann fällt mir auch nichts ein.

D'oh! :wall: Das habe ich gar nicht gesehen... :oops:

btw: In dem Moment, in dem man sich dazu entschließt externe Dateien zu nehmen, ist man immer auch verplichtet sicherzustellen, dass die Werte auch wieder ausgelesen werden können.
Hmpf?
Nun, wenn ich mit deutschen Regionaleinstellungen eine Zahl in einen Text umwandel, erhalte ich "2,34", im englischen "2.34".
Du musst also Standards vorgeben wie das zu passieren hat, oder bestehende standas benutzen. XML wäre hierfür natürlich ideal, aber selbst eine CSV-Datei ist nicht komplett witzlos, wenn man sich an die CSV-Standards hält (also nicht so wie Excel zum Bleispiel).
Wenn dir die etwas komischen XMLs ausreichen, die ein DataSet auspsuckt, dann hast du das Speichern/Lesen von/zu XML schon eingebaut.

Raffigator 4. Jun 2007 12:35

Re: Fließkommazahlen Delphi vs. Oracle
 
Zitat:

Zitat von Der_Unwissende
Ich dachte eigentlich, dass Du die Werte eben als Strings in eine SQL-Anfrage steckst und Oracle dann damit seine Probleme hat. Die Übergabe von umgewandelten Zahlen sollte hingegen kein Problem sein, da sich hier die Anbindung der DB um die korrekte Umwandlung kümmern müsste.

Wo genau das Problem liegt, weiß ich eben selber nicht so genau. Da ich jedoch eine ORA-Fehlermeldung zurückbekomme, gehe ich mal davon aus, dass Oracle das Problem ist.
Also ich benutze die Borland Data Provider und dort weise ich ja jedem Parameter seinen Wert zu (wie oben im kleinen Code-Schnipsel zu sehen).
Angenommen ich wandle meinen eingelesenen String garnicht um, bekomme ich eine Fehlermeldung, dass es keine gueltige Zahl ist. Selbst wenn ich einem Parameter einen konkreten Doublewert zuweise, bekomme ich diese Fehlermeldung! Sobald ich wie gesagt meine NLS_NUMERIC_CHARACTERS in Delphi setzte, bekomme ich keinen Fehler mehr (das lässt mich wiederum daran zweifeln, dass es ein ORACLE-Problem ist!?)


Zitat:

Zitat von Elvis
Nun, wenn ich mit deutschen Regionaleinstellungen eine Zahl in einen Text umwandel, erhalte ich "2,34", im englischen "2.34".
Du musst also Standards vorgeben wie das zu passieren hat, oder bestehende standas benutzen. XML wäre hierfür natürlich ideal, aber selbst eine CSV-Datei ist nicht komplett witzlos, wenn man sich an die CSV-Standards hält (also nicht so wie Excel zum Bleispiel).
Wenn dir die etwas komischen XMLs ausreichen, die ein DataSet auspsuckt, dann hast du das Speichern/Lesen von/zu XML schon eingebaut.

Ich will die Datei nur auslesen und die Datensätze in der Datenbank speichern! Ist dies einmal geschehen, ist die Datei überflüssig und kann gelöscht werden. (altes COBOL-System soll durch neues DB-System ersetzt werden).


Das ganze kann doch nicht so kompliziert sein? Ich bin doch sicher nicht der erste, der Float-Werte in einer Datenbank speichern will!???

Elvis 4. Jun 2007 13:03

Re: Fließkommazahlen Delphi vs. Oracle
 
Zitat:

Zitat von Raffigator
Das ganze kann doch nicht so kompliziert sein? Ich bin doch sicher nicht der erste, der Float-Werte in einer Datenbank speichern will!???

Ist es auch nicht.Du hast auch schon mehrfach gehört wo das Problem ist und wie du es lösen kannst.
Es gab allerdings keine explizite Punkt-für-Punkt Anleitung.
Aber um etwas genauer zu werden: Wenn du die Datei einliest kannst du mit Integer.Parse einen String in eine Zahl umwandeln.
Besonders beachten solltest du den Overload, der einen IFormatProvide bekommt.

Hier mal etwas Pseudo code:
Delphi-Quellcode:
var
  deinNumberFormat : NumberFormatInfo;
begin
  // Clone, weil wir ja nicht das Format für die ganze Anwendung verändern wollen!
  deinNumberFormat := CultureInfo.Create('de').NumberFormat.Clone() as NumberFormat;
  deinNumberFormat.Blabla // ändere die Eigenschaften, die du anders als in "de" brauchst
...
  einDecimal := Decimal.Parse('2,2', deinNumberFormat);
Natürlich willst du nicht ständig ein neues FormatInfo anlegen, aber das ist reine Implementierung und somit deine Sache. :P


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:33 Uhr.

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