Entwickler-Ecke

Sonstiges (Delphi) - Problem bei Typecasting


LuMa86 - Di 14.05.13 23:14
Titel: Problem bei Typecasting
Moin,
ich betreibe Leistungssport, daher führe ich auch ein Trainingsprotokoll. Unser Trainer bekommt das von uns wöchentlich als Excel Datei zugeschickt, um dann einen Monats- bzw. Jahresüberblick zu erstellen/berechnen, funktioniert ja wunderbar mit Excel. Ich habe mir ein Programm geschreiben, was dieses Protokoll ausfüllt. Dafür nutze ich OLE, und das klappt auch alles. Allerdings möchte ich die Excel.DAteien auch lesen können. DAfür habe ich folgendes gemacht:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
{TPData ist ein ClientDataSet, TPDataDauer das Feld, was die Dauer eines Trainings enthält.
Es soll zuerst geprüft werden, ob das Feld überhaupt beschreiben ist, denn wenn das Feld (das ist
übrignes Standartformatiert, nichts besonderes) leer ist, dann entsteht bei dem ".AsFloat" eine Null. 
Diese Null landet dann in den Feldern. Und wenn ich das Protkoll dann wieder speicher, ist alles voller
Nullen, da wo eben eigentlichts nichts steht, bzw. ''.}


if Excel.Cells[row, 8].Value <> '' then // Hier liegt der Fehler
  TPDataDauer.AsFloat := Excel.Cells[row, 8].Value;


Die "if-" Abfrage soll verhinder, das durch das ".AsFloat" aus einem LeerString eine Null wird. Beim abspeichern würden man dann alle leeren Felder in der Excel Datei mit Nullen füllen (sollte man die Excel Datei vorher importiert haben und dabei alle '' zu Nullen gemacht haben). Das ist unübersichtlich und nervig. Allerdings gibt es, wenn ich die "if-" Anweisung NICHT ausklammere/entferne folgenden Fehler: "Exception [...] Variante des Types (UnicodeString) konnte nicht in Typ (Double) konvertiert werden."
Ich hoffe ihr versteht was ich meine und ic hab mich einigermaßen verständlich ausgedrückt :)

Danke :)


Tranx - Mi 15.05.13 06:23

Die Umwandlung von nicht Zahlen in den entsprechenden Excel-Zellen in Zahlen erzeugt immer einen Fehler. Du kannst folgendes machen:


Delphi-Quelltext
1:
2:
3:
4:
5:
try
  TPDataDauer.AsFloat := Excel.Cells[row, 8].Value;          
except
  TPDataDauer.AsFloat := 0;
end;


Dann fängt Delphi den Fehler ab und macht dann - statt die Variable mit dem Wert - der ja keiner ist (z.B. '' oder 'Startzeit' ....) das, was Du willst. Du kannst den Except-Teil auch leer lassen. Ich denke, Du willst bei "fehlerhaften" Zellen keine Datenübergabe machen. Dann fällt die Anweisung TPDataDauer.AsFloat := 0; einfach weg. Der Fehler wird durch das try weggefangen.


jaenicke - Mi 15.05.13 06:50

Wie wäre es mit TryStrToFloat?


jasocul - Mi 15.05.13 17:26

Oder StrToFloatDef?


jaenicke - Mi 15.05.13 19:28

Das würde ja gerade die Nullen in die Felder schreiben, was ja nicht gewünscht ist, soweit ich das verstanden habe. Vielleicht lässt sich auch mit VarIsNull oder so abfragen, ob da ein Wert ist.


Gerd Kayser - Mi 15.05.13 20:39

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Vielleicht lässt sich auch mit VarIsNull oder so abfragen, ob da ein Wert ist.

Man kann abfragen, was für ein Typ im Variant enthalten ist.
Beispiel:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
procedure TMainform.Button2Click(Sender: TObject);
var
  Excel       : Variant;
  Blatt       : Variant;
  ExcelWS     : Variant;
  Ergebnis    : Variant;
  ErgebnisTyp : integer;
  StringTyp   : string;
begin
  try
    Excel := GetActiveOleObject('Excel.Application');
  except
    Excel := CreateOleObject('Excel.Application');
  end;
  Excel.Visible := True;

  Excel.WorkBooks.Open('C:\Test.xlsx');
  Blatt := Excel.Worksheets.Item['Tabelle1'];

  Blatt.Activate;
  ExcelWS := Excel.ActiveSheet;

  Ergebnis := ExcelWS.Cells[12].Value;  // Zeile 1, Spalte 2
  ErgebnisTyp := VarType(Ergebnis) and VarTypeMask;

  // Bei Excel kommen nur wenige davon in Betracht. Einfach ausprobieren.

  case ErgebnisTyp of
    varEmpty     : StringTyp := 'varEmpty';      // Excel
    varNull      : StringTyp := 'varNull';
    varSmallInt  : StringTyp := 'varSmallInt';
    varInteger   : StringTyp := 'varInteger';
    varSingle    : StringTyp := 'varSingle';
    varDouble    : StringTyp := 'varDouble';     // Excel
    varCurrency  : StringTyp := 'varCurrency';   // Excel
    varDate      : StringTyp := 'varDate';       // Excel
    varOleStr    : StringTyp := 'varOleStr';     // Excel
    varDispatch  : StringTyp := 'varDispatch';
    varError     : StringTyp := 'varError';
    varBoolean   : StringTyp := 'varBoolean';
    varVariant   : StringTyp := 'varVariant';
    varUnknown   : StringTyp := 'varUnknown';
    varByte      : StringTyp := 'varByte';
    varWord      : StringTyp := 'varWord';
    varLongWord  : StringTyp := 'varLongWord';
    varInt64     : StringTyp := 'varInt64';
    varStrArg    : StringTyp := 'varStrArg';
    varString    : StringTyp := 'varString';
    varAny       : StringTyp := 'varAny';
    varTypeMask  : StringTyp := 'varTypeMask';
  end;

  ShowMessage('StringTyp:   ' + StringTyp);

  // Excel.Quit;                    // Excel beenden.
end;


Quelle: http://www.delphibasics.co.uk/RTL.asp?Name=VarType


jasocul - Mi 15.05.13 22:07

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Das würde ja gerade die Nullen in die Felder schreiben, was ja nicht gewünscht ist, soweit ich das verstanden habe.

Dann nimmt man als Fehlerwert eben "0" und vergleicht dann das Ergebnis auf "0". In dem Fall wird die Zuweisung übersprungen.
Es geht ja hier auch darum, dass ein paar mehr Werte übernommen werden sollen. Das mit einer Try-Variante zu machen ist nicht besonders performant. Wobei ich zugebe, dass ich die Performanz von TryStrToFloat nicht geprüft habe.
Ich halte die Lösung von user profile iconTranx jedenfalls nicht für besonders gut. Try..Except sollte wirklich nur Ausnahmen behandeln.


jaenicke - Do 16.05.13 06:16

user profile iconjasocul hat folgendes geschrieben Zum zitierten Posting springen:
Das mit einer Try-Variante zu machen ist nicht besonders performant. Wobei ich zugebe, dass ich die Performanz von TryStrToFloat nicht geprüft habe.
Ich halte die Lösung von user profile iconTranx jedenfalls nicht für besonders gut. Try..Except sollte wirklich nur Ausnahmen behandeln.
Öhm... wenn es um die Performance geht, solltest du dir mal dringend die beiden Funktionen anschauen... Die haben rein gar nichts mit try..except zu tun und nutzen intern den selben Code.
Denn:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
function StrToFloatDef(const S: stringconst Default: Extended;
  const AFormatSettings: TFormatSettings): Extended;
begin
  if not TextToFloat(PChar(S), Result, fvExtended, AFormatSettings) then
    Result := Default;
end;

function TryStrToFloat(const S: stringout Value: Extended;
  const AFormatSettings: TFormatSettings): Boolean;
begin
  Result := TextToFloat(PChar(S), Value, fvExtended, AFormatSettings);
end;
:wink:

Deshalb ist deine Lösung mit StrToFloatDef sogar langsamer, weil erstens intern geprüft wird, ob die Umwandlung erfolgreich ist und dann der Defaultwert genommen wird und du dann zusätzlich erneut vergleichst und ggf. anders zuweist.


jasocul - Do 16.05.13 07:09

Ich hatte ja geschrieben, dass ich mir die Funktion noch nicht angesehen hatte. Durch die Namensgebung bin ich von einer falschen Annahme ausgegangen. Danke für deine Aufklärung. :beer:


LuMa86 - So 19.05.13 18:25

Ah, von "Try" hatte ich noch nie gehört. Damit geht es wunderbar :) Danke