Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Datentyp zur Laufzeit in Object Property/Variable festlegen
ml-kuen - So 29.06.14 22:36
Titel: Datentyp zur Laufzeit in Object Property/Variable festlegen
Hallo,
hin und wieder habe ich hier schon Hilfe bekommen. Ich schätze dieses Forum auch wegen des tollen Supports. Eben gerade war mein Account noch gesperrt. Zwei Minuten später hatte ich schon die Lösung von euch (:-)@entwickler-ecke.de) im Postfach.
Aber nun zu meinem Problem. Ich möchte Daten aus einer strukturierten Textdatei lesen und die Datensätze (jeweils eine Zeile) in ein TObjekt-Descendent speichern. In den Daten gibt es zwei Felder welche den Datentyp angeben und ob dieser einfach oder mehrfach (Liste) vorkommt. Mit meinen eigenen Kenntnissen komme ich da leider nicht weiter. Deshalb gibt's dazu auch kein Quelltext von mir. Ich habe nur eine wage Idee, dass man dazu eventuell procedurale Typen verwenden könnte.
Hätte vielleicht jemand ein Beispiel oder eine Quelle für den aha-Effekt für mich.
MfG
mandras - So 29.06.14 22:54
Auf die Schnelle zusammengebastelt, ich weiß ja nicht viel über das was Du vorhast:
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:
| uses Contnrs;
type TEintrag = class(TObject) A: string; B: integer; end;
procedure TForm1.Button1Click(Sender: TObject); var DieListe: TObjectList; NeuEintrag: TEintrag; EinEintrag : TEintrag; begin DieListe := TObjectList.Create; NeuEintrag := TEintrag.Create; NeuEintrag.A := 'Hallo'; NeuEintrag.B := 17; DieListe.Add(NeuEintrag); EinEintrag := DieListe[0] as TEintrag; Application.MessageBox(pchar(format ('Eintrag: %s Wert: %d',[EinEintrag.A,EinEintrag.B])),'Ergebnis');
end; |
ml-kuen - So 29.06.14 23:20
Danke für die Antwort,
hab mich offenbar nicht richtig ausgedrückt. Die Textdatei hat unter anderem fogende Struktur:
Quelltext
1: 2: 3: 4:
| ... Add Optiondef 2 "Time Offset" DWORD 0 comment="UCT offset in seconds" 0 ... Add Optiondef 3 "Router" IPADDRESS 1 comment="Array of router addresses ordered by preference" 0.0.0.0 ... ... Add Optiondef 17 "Root Path" STRING 0 comment="Path name for client's root disk, char set NVT ASCII" "" |
es handelt sich um einen Auszug eines DHCP-Server-Dumps (netsh dhcp \\server dump).
Im Feld 0 und 1 steht, dass eine Optionsdefinition hinzugefügt wird.
Im Feld 2 steht die Options-ID.
Im Feld 3 steht der Name der Option.
Im Feld 4 steht der Datentyp des letzten/der letzten Felder.
Im Feld 6 steht ein Kommentar
Im Feld 5 steht, ob die Daten Einzelwerte (0) oder Mehrfache Werte (1) sind.
Vorkommende Datentypen sind: 'BINARY', 'BYTE', 'WORD', 'DWORD', 'IPADDRESS' und 'STRING'.
Nun sollten hoffentlich alle Klarheiten beseitigt sein :wink:
freak4fun - So 29.06.14 23:34
Datei Zeilenweise einlesen oder auch in StringList(StrictDelimiter), dann anhand eines Trennzeichens (" ") den String teilen und die einzelnen Segmente entsprechend behandeln. ;)
ml-kuen - So 29.06.14 23:41
Hallo
freak4fun,
hast du dir die Frage wirklich durchgelesen? Langsam zweifle ich...
ssb-blume - Mo 30.06.14 09:08
Zuerst je Zeile den Text mit "..." suchen und löschen. Dann wie freak4fun beschrieben Rest mit stringparse (weiß nicht, wie in Delphi das heist) mit den Leerzeichen teilen, in dem erzeugten Array stehen dan die gesuchten werte.
Hansi
Quitzlinga - Mo 30.06.14 09:18
Hi,
evt. hilft Dir für die Abbildung ein
VARIANTER RECORD weiter.
Ansonsten würde ich es folgendermassen machen:
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:
| type TOptionDatatype = (optBINARY, optBYTE, optWORD, optDWORD, optIPADDRESS, optSTRING);
type TOption = class(TObject) ID : integer; OName : string; Datatype : TOptionDatatype; Data : TObjectlist; Einzelwerte : boolean; Kommentar : string; private public constructor Create();overload; destructor Destroy();override; end;
type TOptionList = class(TObjectList) private public function LoadFromFile(aFilename : string) : boolean; end;
constructor TOption.Create(); begin inherited Create(); Data := TObjectlist.Create(); end;
destructor TOption.Destroy(); begin FreeAndNil(Data); inherited Destroy(); end;
function TOptionList.LoadFromFile(aFilename : string) : boolean; begin end; |
MfG
Quitzlinga
WasWeißDennIch - Mo 30.06.14 09:43
Eine varianten Record hatte ich auch gleich im Hinterkopf:
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:
| type TMyDataType = (dtWord, dtDWord, dtString); TMyData = record DataType: TMyDataType; case integer of 0:( WordData: Word ); 1:( DWordData: DWord ); 2:( StringData: Shortstring ); end;
procedure ShowData(const Data: TMyData); begin case Data.DataType of dtWord: ShowMessage(Format('Die Worddaten enthalten %d.', [Data.WordData])); dtDWord: ShowMessage(Format('Die DWorddaten enthalten %d.', [Data.DWordData])); dtString: ShowMessage(Format('Die Stringdaten enthalten "%s".', [Data.StringData])); end; end;
procedure TFormTest.Button1Click(Sender: TObject); var Data: array[0..2] of TMyData; i: integer; begin Data[0].DataType := dtWord; Data[0].WordData := 65535; Data[1].DataType := dtDword; Data[1].DWordData := 42; Data[2].DataType := dtString; Data[2].StringData := 'Hallo Welt'; for i := Low(Data) to High(Data) do ShowData(Data[i]); end; |
Blup - Mo 30.06.14 11:04
Ich vermute das Grundwissen fehlt, wie man so einen Text erst mal zerlegt.
Die Objektstruktur, in die diese Daten überführt werden sollen, ist noch mal eine andere Baustelle.
Ungetestet ein kleines 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: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98:
| const CRLF = #13#10;
type TZeileObject = class(TObject) private FComment: string; published property Comment: string read FComment write FComment; end;
TTimeOffsetObject = class(TZeileObject) private FValue: DWord; published property Value: DWord read FValue write FValue; end;
procedure CheckSpalte(ASpalte: Integer; Spalten: TStringList; const ACheckValue: string); begin if Spalten[ASpalte] <> AValueFound then raise Exception.CreateFmt('Ungültiger Wert in Spalte %d erwartet "%s" gefunden "%s".', [ASpalte + 1, ACheckValue, Spalten[ASpalte]]); end;
procedure ImportTyp2(Spalten: TStringList; AItemList: TObjectList); var lData: TTimeOffsetObject; begin lData := nil; try if Spalten.Count < 8 then raise Exception.CreateFmt('Spaltenanzahl(%d) zu gering', [Spalten.Count]);
CheckSpalte(3, Spalten, 'Time Offset'); CheckSpalte(4, Spalten, 'DWORD'); CheckSpalte(5, Spalten, '0');
lData := TTimeOffsetObject.Create; lData.Comment := Spalten.Values['comment']; lData.Value := StrToInt(Spalten[7];); except on E: Exception do begin lData.Free; E.Message := Format('ImportTyp2' + CRLF) + E.Message; raise; end; end; AItemList.Add(lDate); end;
procedure ImportFile(const AFilename: string; AItemList: TObjectList); var Zeilen, Spalten: TStringList; iZeile: Integer; begin Zeilen := nil; Spalten := nil; try Zeilen := TStringList.Create; Zeilen.LoadFromFile(AFilename);
Spalten := TStringList.Create; Spalten.QuoteChar := '"'; Spalten.Delimiter := ' '; Spalten.StrictDelimiter := True;
for iZeile := Low(Zeilen) to High(Zeilen) do begin try Spalten.DelimitedText := Zeilen[iZeile]; case Spalten.Count of 0: Continue; 1..5: raise Exception.CreateFmt('Spaltenanzahl(%d) zu gering', [Spalten.Count]); end;
case IntToStrDef(Spalten[2], -1) of 2: ImportTyp2(Spalten, AItemList); 3: ImportTyp3(Spalten, AItemList); 17: ImportTyp17(Spalten, AItemList); else raise Exception.CreateFmt('Spalte 3 ImportTyp ''%s'' ist nicht definiert', [Spalten[2]]); end;
except on E: Exception do begin E.Message := Format('Import-Fehler in Zeile %d' + CRLF, [iZeile + 1]) + E.Message; raise; end; end; end; finally Spalten.Free; Zeilen.Free; end; end; |
WasWeißDennIch - Mo 30.06.14 11:14
Also, ich lese aus der Frage heraus, dass es nicht um Stringzerlegung geht, sondern darum, verschiedene Datentypen möglichst einheitlich zu handlen.
ml-kuen - Mo 30.06.14 21:18
Vielen Dank
WasWeißDennIch, endlich hat jemand gecheckt was ich möchte. Wie man die String-Zeile zerlegt weiß ich und das ist auch nicht das Problem. Allerdings sind es viele verschiedene Datentypen und die Werte sind manchmal als Liste zu speichern. Die Textzeilen (siehe oben) sind halt nur eine Definition der Werte die dann weiter unten in der Datei noch kommen. Identifiziert werden die zugehörigen Werte dort durch die Options-ID (siehe oben).
Die Wertezeilen sehen etwa so aus:
Quelltext
1: 2: 3:
| ... set optionvalue 2 DWORD 3600 ... set optionvalue 3 IPADDRESS 192.168.0.1 192.168.0.254 ... set optionvalue 17 STRING "\\rechnername.domain.net\Freigabe" |
Beispielsweise ist die Optionvalue 2 der Time- Offset. Da dieser Wert nur einmal vorkommen kann steht im Feld 5 auch eine 0 (einzelner Wert).
In der nächsten Zeile ist die Optionvalue 3 und damit von der Definition (siehe oben) die Routeradresse. Da es davon bekanntlich mehrere geben kann, steht in der Definition oben auch eine 1 im Feld 5. Optionvalue 17 ist wieder ein Einzelwert im String-Format... usw. Es können bis zu 255 Optionen existieren.
Ich möchte nun aber nicht für jede Kombination von Datentyp und Wertehäufigkeit ein eigenes Objekt erstellen - wenn's möglich ist. Vermutlich muss ich wohl doch einen Varianten Typ in eine Liste stecken, welche dann beides beherrscht. Einen oder mehrere Werte von einem Typ, den Variant halt zulässt. IPADDRESS ließe sich in DWORD verstecken, Strings in Strings und die anderen fundamentalen Typen sollten mit Variant auch funktionieren.
Gibt's, abgesehen von Variant, noch andere Möglichkeiten?
Gruß
icho2099 - Di 01.07.14 00:11
Ich würde es bei den Strings belassen und diese zusammen mit den Type-defs in eine stringliste
schreiben. Alle Datenfelder einer Zeile sind doch vom gleichen Typ.
Was willst du denn mit den so gewonnenen Daten dann noch tun?
ml-kuen - Di 01.07.14 00:46
Hallo
icho2099,
mit IP-Adressen kann man rechnen - Anzahl Hosts in einem Subnet - nächste freie IP herausfinden und und und. Mit den anderen Datentypen ausser String selbverständlich auch. Die Sortierung einer IP oder Zahl ist auch anders als die alphabetische.
Du hast ja Recht, wenn du mit deiner Anregung meinst, man sollte nicht "mit Kanonen auf Spatzen schießen". Leider musste ich aber schon oft feststellen, dass sich diese herangehensweise bei einer objektorientierten Programmieraufgabe irgendwann rechen kann. Ich bin ja nur ein Hobby-Programmierer und tue dies aus Interesse und Freude am verstehen. Natürlich soll auch etwas sinnvolles dabei rauskommen. Aber anders als bei einem Profi mit Pflichtenheft usw. wächst bei mir das Programm manchmal im Kopf weiter und man hat Spaß dabei die Dinge weiterzuspinnen.
Gruß
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!