AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten
Thema durchsuchen
Ansicht
Themen-Optionen

TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

Ein Thema von cltom · begonnen am 30. Apr 2022 · letzter Beitrag vom 2. Mai 2022
Antwort Antwort
Seite 1 von 2  1 2      
cltom

Registriert seit: 22. Sep 2005
221 Beiträge
 
Delphi 12 Athens
 
#1

TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

  Alt 30. Apr 2022, 14:40
Delphi-Version: XE2
Hallo,

ein Thema, das bestimmt zigfach gelöst ist, ich hab aber keine gute Lösung dazu, die nicht einen irren Aufwand verursacht.

Es sollen Werte aus Edit-Feldern in diverse Variablen geschrieben werden. Dafür wird TryStrToInt/TryStrToFloat verwendet. Die Werte werden numerisch gebraucht für einige Rechnungen. Dann wird alles in eine SQL-DB geschrieben. Später geht der Weg natürlich wieder retour, aus der SQLite ins Objekt, von da wieder ins UI via FloatToStr(...).

Nun kann es sein, dass manche Eingabefelder leer bleiben, der User also einfach nichts eingibt. Das soll auch erlaubt sein. Allerdings: es soll nun beim Lesen auch wieder nichts drinstehen, nicht "0". Wie kriegt man das hin, dass entlang obiger Kette das "nichts" erhalten bleibt und nicht eine 0 rauskommt.


Die Themen fangen schon damit an, dass beim TryStrToFloat bei einem ungültigen String als Eingabe ja letztlich ein Wert in der Variable vorhanden ist. Beim TryStrToInt kommt Null raus.


Die Varianten, die mir mal eingefallen sind:

1. das Ergebnis von TryStrToInt/TryStrToFloat verwenden
TryStrToInt/Float hab ich ja schon, da kann ich ja das result nehmen. Aber wie weiter? Ich müsste ja dann für jede Variable einen bool mitführen, der das Ergebnis von TryStrToInt enthält, um später zu entscheiden, ob der Wert in die DB geschrieben wird. Denn sobald Null drinsteht, landet Null in der DB und ich kann später nicht mehr sagen, ob die Null eine Eingabe war oder beim TryStrToInt entstanden ist. Die SQL-Anweisungen werden dann allerdings ein absurder Aufwand, weil ich für jede Variable einzeln prüfen muss, ob ihr zugehöriger "bin-eine-echte-Null"-bool wahr ist. Allein die Prüfung TryStrToInt verhindert ja nicht, dass in der Variable eine Null landet, wenn der string='' ist
Variation: den bool führen und zusätzlich mit in die DB aufnehmen. Dann bleibt der SQl-Zugriff unberührt ... enorm aufgebläht um zahhlose bools zwar ... schick ist anders.

2. bei einem false von TryStrToX einen "Markerwert" in die Variable setzen
Also beim false von TryStrToX der Variable einen Wert verpassen, der exotisch genug ist, dass er real sonst nicht vorkommt. Damit kann man alles machen, die DB-Zugriffe ändern sich nicht. Da könnte man wohl die "def"-Variante verwenden. Beim Züruckholen ins UI wird der Wert abgefangen und durch '' ersetzt. Nachteile: es wird ein falscher Wert geführt, der in den Rechnungen Probleme machen könnte. Da müsste man allerhand Unsinn abfangen. Die Datenbank enthält falsche Werte, die bei allen folgenden Abfragen/Rechnungen/etc. berücksichtigt werden müssten und letztlich womöglich eines Tages keiner weiß, warum ein Teil der Felder merkwürdige Zahlen enthält.

3. alle Eingaben grundsätzlich mal als String abspeichern. Dann ist mal die Originaleingabe erhalten. Für die Rechnung brauch ich die Umwandlung dennoch, dh die Variablenzahl verdoppelt sich. In der DB hab ich dann entweder nur noch strings und bin damit eingeschränkt oder alles doppelt. Einmal als string, einmal numerisch. Auch hier: der Aufwand ist groß, die Struktur in der Datenbank alles andere als elegant



Hab bestimmt was Eleganteres übersehen, oder?

danke

Gruß
cltom
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.508 Beiträge
 
Delphi 7 Professional
 
#2

AW: TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

  Alt 30. Apr 2022, 15:09
Unausgegorene Gedankensplitter von mir:
Delphi-Quellcode:
  // Schreiben in die DB:
  // statt
  if TryStrToFloat(Edit.Text, myFloat) then
    qry.FieldByName('Nummer').AsFloat := myFloat;
  // mal versuchen
  qry.FieldByName('Nummer').AsVariant = Edit.Text;
  // statt
  if TryStrToInt(Edit.Text, myInt) then
    qry.FieldByName('Nummer').AsInteger := myInt;
  // mal versuchen
  qry.FieldByName('Nummer').AsVariant := Edit.Text;

  // statt
  if TryStrToFloat(Edit.Text, myFloat) then
    qry.ParamByName('Nummer').AsFloat := myFloat;
  // mal versuchen
  qry.ParamByName('Nummer').Value = Edit.Text;
  // statt
  if TryStrToInt(Edit.Text, myInt) then
    qry.ParamByName('Nummer').AsInteger := myInt;
  // mal versuchen
  qry.ParamByName('Nummer').Value := Edit.Text;

  // Lesen aus der DB:
  Edit.Text := qry.FieldByName('Nummer').AsVariant;
Varianten machen eine implizite Typkonvertierung, wenn Edit.Text leer ist, könnte damit dann Null in die DB geschrieben werden. Müsstest Du mal prüfen, keine Ahnung, ob sich da alle Datenbank(schnittstell)en gleich verhalten.
Beim Lesen aus der DB wird der Inhalt des Datenbankfeldes bei der Zuweisung per .AsVariant in den Typ konvertiert, der links vom Gleichheitszeichen steht. Bei Edit.text also in einen String.

Die delphitypische Typsicherheit könnte dabei allerdings verloren gehen.

Geändert von Delphi.Narium (30. Apr 2022 um 15:12 Uhr)
  Mit Zitat antworten Zitat
cltom

Registriert seit: 22. Sep 2005
221 Beiträge
 
Delphi 12 Athens
 
#3

AW: TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

  Alt 30. Apr 2022, 15:41
Danke Dir!

ok, wär ein Ansatz, danke Dir. Allerdings hab ich aktuell alle Variablen in einem Objekt und schick das Objekt an die SQL-Query, wo dann die Variablen geschrieben werden. Das UI hat niemals alle Werte gleichzeitig angezeigt, dh ich muss die Werte mal alle holen, im Objekt lagern und damit später die Query befüttern.

Aber vermutlich ließe sich Dein Gedanke weiterspinnen, ob man grundsätzlich auf Typensicherheit verzichten könnte? Erscheint mir auch ein hoher Preis, zumal ganz sicher irgendwo am Weg (es werden noch Diagramme gezeichnet, etc.) Probleme auftauchen könnten, wenn da einzelne Komponenten doch float/double/... sehen wollen.
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

  Alt 30. Apr 2022, 16:00
Stichwort nullable types:
https://dalijap.blogspot.com/2020/05...m-managed.html

Damit kannst du einen Leerstring auch als Nullwert speichern und so auch in die DB schreiben.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#5

AW: TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

  Alt 30. Apr 2022, 16:13
Allerdings hab ich aktuell alle Variablen in einem Objekt und schick das Objekt an die SQL-Query, wo dann die Variablen geschrieben werden.
Unterstütz das DAC eine interne Update-Funktionalität (wie z.B. TFDQuery das tut), dann kann man dem TIntegerField einfach über AsString den Wert mitgeben (AsVariant geht glaube ich nicht so gut). Ist der String leer, wird NULL an die Datenbank übergeben, andernfalls der entsprechende Wert. Umgekehrt wird beim Lesen über AsString ein NULL in der DB als Leerstring zurückgegeben. Damit wird in der DB schon mal zwischen NULL und 0 unterschieden und dies auch im Edit-Control entsprechend dargestellt und ausgewertet.

Leider ist der Aufwand deutlich höher, wenn man die Query direkt zusammenbaut oder über ein ORM-ähnliches System darauf zugreift. Letzteres würde ein ORM erfordern, das auch Nullable Types unterstützt.

Obwohl mittlerweile eher verpönt, unterstützen datensensitive Controls diese Anforderung schon ewig von Haus aus.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.508 Beiträge
 
Delphi 7 Professional
 
#6

AW: TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

  Alt 30. Apr 2022, 16:32
Bei meinem Ansatz mit Variant geht die Typsicherheit mit Sicherheit flöten und Nebenwirkungen sind gerade bei Float (zufällig ohne Nachkommastellen) und Integer eher zu erwarten als auszuschließen.

Alternative:

Die Routine, die UI ins Objekt / DB schreibt, prüft ob das UI-Feld leer ist.
Delphi-Quellcode:
  if Edit.Text = 'then
    qry.FieldByName('Nummer').AsVariant := EmptyParam // Das müsste in der DB dann NULL werden.
  else
  begin
    if TryStrToFloat(Edit.Text, myFloat) then
      qry.FieldByName('Nummer').AsFloat := myFloat
    else
      // was ist hier im Fehlerfalle zu machen?
  end;
Bei leerem Edit.Text wird qry.FieldByName('Nummer').AsString := Edit.Text eher als Leerstring in der DB landen als als Null. Aber hier könnte es je nach Datenbank(schnittstelle) auch Unterschiede geben.

qry.FieldByName('Nummer').AsString := Edit.Text funktioniert allerdings auch, wenn Nummer in der DB als Integer oder Float definiert ist. Enthält Edit.Text allerdings etwas, was nicht zum Typ von Nummer passt, wird es eine Exception geben.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#7

AW: TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

  Alt 30. Apr 2022, 16:59
Bei leerem Edit.Text wird qry.FieldByName('Nummer').AsString := Edit.Text eher als Leerstring in der DB landen als als Null.
Zumindest bei einem TIntegerField wird es das eben nicht. Dort wird in SetAsString explizit ein Clear aufgerufen wenn der String leer ist. Das setzt allerdings voraus, dass in der Datenbank auch wirklich ein Integer Feld steht und kein String Feld. Aber das entnehme ich auch so der bisherigen Beschreibung.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
cltom

Registriert seit: 22. Sep 2005
221 Beiträge
 
Delphi 12 Athens
 
#8

AW: TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

  Alt 30. Apr 2022, 17:39
Als DB-Schnittstelle kommt aktuell (noch) ZeosDB zum Einsatz, künftig vermutlich FireDAC (Update auf 11.1 steht auch ins Haus, dabei gleich mit FireDAC). Als zusätzlich Komplikation: aktuell wird noch in eine lokale SQLite geschrieben, bald mal auf einen SQL-Server.

Datensensitive Controls hab ich lange überlegt, dann aber nicht verwendet ... allen voran wohl weil ich nicht abschätzen konnte, wie aufwändig das werden würde, wenn ich viele der Felder für diverse Rechnungen brauche oder sich einige der Felder auch erst aus Berechnungen ergeben. Kann man sicher lösen, aber ich hatte mich nicht drüber getraut. Aktuell repräsentieren meine Objekte recht schön den realen Anwendungsfall.

Betreffend Nullable Types: ist es am Ende nicht genau das Szenario, dass man zu jeder Variable ein bool mitführt, das sagt, ob die Variable gesetzt wurde? Dann muss man beim Schreiben also den Status dieses Bools abfragen und dann entscheiden, ob man die Variable in den SQL Befehl aufnimmt. Nicht nur, dass man muss sie im Grunde auch mit in die SQL schreiben, um sicher zu gehen, dass beim Lesen aus der DB nicht erst recht wieder ein 0 als Ergebnis in die Variable kommt.
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.508 Beiträge
 
Delphi 7 Professional
 
#9

AW: TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

  Alt 30. Apr 2022, 17:44
Bei leerem Edit.Text wird qry.FieldByName('Nummer').AsString := Edit.Text eher als Leerstring in der DB landen als als Null.
Zumindest bei einem TIntegerField wird es das eben nicht. Dort wird in SetAsString explizit ein Clear aufgerufen wenn der String leer ist. Das setzt allerdings voraus, dass in der Datenbank auch wirklich ein Integer Feld steht und kein String Feld. Aber das entnehme ich auch so der bisherigen Beschreibung.
Du hast recht, bei 'nem Stringfeld in der DB wird qry.FieldByName('Nummer').AsString := Edit.Text eher als Leerstring in der DB landen als als Null.
Aber bei nummerischen Feldern in der DB ist dort ja letztlich auch kein Leersting in der DB möglich, also muss da "irgendwas anderes für das Feld passendes" gefunden werden. Und das ist bei Integer ... halt leider nicht Null sondern 0. Und damit sind wir dann bei unserem Ursprungsproblem.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#10

AW: TryStrToFloat/TryStrToInt, leeren String nicht als 0 erhalten

  Alt 30. Apr 2022, 18:03
Aber bei nummerischen Feldern in der DB ist dort ja letztlich auch kein Leersting in der DB möglich, also muss da "irgendwas anderes für das Feld passendes" gefunden werden. Und das ist bei Integer ... halt leider nicht Null sondern 0.
Diese Aussage wage ich zu bezweifeln. Auch ein Integer-Feld in der Datenbank kann natürlich NULL sein. Es ist allerdings so, dass TIntegerField über AsInteger oder Value in dem Fall 0 liefert. Was soll es auch anders tun, wenn ein Integer abgefragt wird. Deswegen hatte ich ja auch auf AsString für das Schreiben und Lesen hingewiesen. Das liefert nämlich bei NULL wieder einen Leerstring und nicht '0'.

Das Problem ist also nicht die DB oder das TEdit, auch nicht Zeos, FireDAC oder TIntegerField. Wenn das nicht funktioniert liegt der Fehler woanders.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:04 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