Autor |
Beitrag |
ml-kuen
Beiträge: 17
Win 95, Win 98, Win 2000, Win XP, Win 7
Delphi 7 personal
|
Verfasst: So 29.06.14 22:36
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
Beiträge: 430
Erhaltene Danke: 107
Win 10
Delphi 6 Prof, Delphi 10.4 Prof
|
Verfasst: 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
Beiträge: 17
Win 95, Win 98, Win 2000, Win XP, Win 7
Delphi 7 personal
|
Verfasst: 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
Zuletzt bearbeitet von ml-kuen am Mo 30.06.14 21:50, insgesamt 1-mal bearbeitet
|
|
freak4fun
Beiträge: 604
Erhaltene Danke: 4
Win 7 Pro
VS 2013 Express, Delphi, C#, PHP, Java
|
Verfasst: 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.
_________________ "Ich werde auf GAR KEINEN Fall…!" - "Keks?" - "Okay, ich tu's."
i++; // zaehler i um 1 erhoehen
|
|
ml-kuen
Beiträge: 17
Win 95, Win 98, Win 2000, Win XP, Win 7
Delphi 7 personal
|
Verfasst: So 29.06.14 23:41
Hallo freak4fun,
hast du dir die Frage wirklich durchgelesen? Langsam zweifle ich...
|
|
ssb-blume
Beiträge: 375
Erhaltene Danke: 7
XP, W7, W8
Deutschland
|
Verfasst: 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
_________________ Brain: an apparatus with which we think we think.
|
|
Quitzlinga
Beiträge: 60
Erhaltene Danke: 2
Win XP
Delphi 2007 Prof. Codegear Win32
|
Verfasst: 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:
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
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: Mo 30.06.14 09:43
Eine varianten Record hatte ich auch gleich im Hinterkopf:
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
Beiträge: 174
Erhaltene Danke: 43
|
Verfasst: 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:
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
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: 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
Beiträge: 17
Win 95, Win 98, Win 2000, Win XP, Win 7
Delphi 7 personal
|
Verfasst: 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
Beiträge: 101
Erhaltene Danke: 12
WIN XP, WIN 7, WIN 10
Delphi 6 Prof, Delphi 2005, FPC
|
Verfasst: 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
Beiträge: 17
Win 95, Win 98, Win 2000, Win XP, Win 7
Delphi 7 personal
|
Verfasst: 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ß
|
|
|