![]() |
Datei mit dynamischen Array einlesen
Hallo allerseits,
Ich steh vor einem Problem und weiß nicht, wie ich es lösen könnte, da immer beim Dimensionieren ein Fehler auftritt, vielleicht kann mir wer weiterhelfen: Ich möchte ein File als stream einlesen, das zuerst aus einem statischen Header besteht, dann aber je nach Inhalt dynamisch wird: type TFileHeader = packed record FileCode :LongInt; [...] end; // ist 100 Bytes groß nach diesem statischen Header kommt immer ein Record Header mit folgender Struktur: type TRecordHeader = packed record ID : LongInt; // Big Endian 4 Byte Laenge : LongInt; // Big Endian 4 Byte end; ID zählt einfach beginnend von 1 hinauf, Laenge gibt die Größe des nachfolgenden Records an. danach kommt eben der Objekt-Record, der je nach Wert einer bestimmten Variable im FileHeader eine andere Struktur hat (es handelt sich hierbei um .shp - ESRI Shapefiles): Mein Problem taucht bei folgender Struktur auf: type TObjektRecord = packed record ShapeType : LongInt; Box : TBox; // ist x1,y1,x2,y2 of Double = Rechteck NumParts : LongInt; NumPoints : LongInt; Parts : Array of LongInt; Points : Array of TPunktXY; // ist X,Y : Double end; NumParts sagt mir, wie viele Elemente im Array Parts sind. NumPoints sagt mir, wie viele Elemente im Array Points sind. Wenn ich jetzt eben hergehe und sage: var Strom : TFileStream; try Strom:=TFileStream.Create(Dateiname, fmOpenRead); Strom.Read(FileHeader, SizeOf(FileHeader)); except [...] end; alles kein Problem, jedoch repeat // RecordHeader einlesen Strom.Read(RecordHeader, SizeOf(RecordHeader)); Strom.Read(varShapeType, SizeOf(varShapeType)); Strom.Read(varBox, SizeOf(varBox)); Strom.Read(varNumParts, SizeOf(NumParts)); Strom.Read(varNumPoints, SizeOf(NumPoints)); SetLength(varParts, NumParts); <--------------- Hier kommt dann spätestens die AV, Programmabbruch SetLength(varPoints, NumPoints); Strom.Read(varParts, SizeOf(varParts)); Strom.Read(varPoints, SizeOf(varPoints)); until (Strom.Position>FileHeader.FileLen); Was mache ich falsch, wo ist mein Denkfehler drin? Vielen Dank im Voraus!! LG Alex |
AW: Datei mit dynamischen Array einlesen
Zunächst mal verwende überall die Methode
Delphi-Quellcode:
anstatt
ReadBuffer()
Delphi-Quellcode:
:
Read()
Delphi-Quellcode:
Falls über das Ende des Streams hinausgelesen wird, wird eine Exception ausgelöst, was ein wichtiger Hinweis ist.
Strom:=TFileStream.Create(Dateiname, fmOpenRead);
Strom.ReadBuffer(FileHeader, SizeOf(FileHeader)); Würde man über das Ende hinauslesen, es aber nicht bemerken, können natürlich alle möglichen Fehler entstehen. |
AW: Datei mit dynamischen Array einlesen
Müsste es an der Stelle mit der AV nicht "Irgendwas.NumParts" heißen, da NumParts doch Teil eines Records ist?
Vielleicht geht bei den vorherigen Verwendungen der Variable alles gut, weil nicht wirklich etwas damit gemacht wird, sondern nur die Größe abgefragt wird. Ein with-Statement verwendest Du doch nicht? - Jedenfalls ist das aus dem Codebeispiel nicht ersichtlich. |
AW: Datei mit dynamischen Array einlesen
Du liest in eine Variable "varNumParts" die Größe des Arrays ein und verwendest danach eine andere Variable "NumParts" um die Größe des Arrays zu bestimmen. Ich vermute die Variable "NumParts" ist Teil eines Records der noch nicht erzeugt wurde.
Delphi-Quellcode:
Strom.Read(varNumParts, SizeOf(NumParts));
{...} SetLength(varParts, NumParts); <--------------- Hier kommt dann spätestens die AV, Programmabbruch |
AW: Datei mit dynamischen Array einlesen
Zitat:
Delphi-Quellcode:
Und ich traue Deiner Record-Definition nicht.
gelesen:=Strom.Read(buffer,sizeof(buffer));
if gelesen<>sizeof(buffer) then showmessage('Weniger Daten als erwartet gelesen'); Aus eigener schlechter Erfahrung ist ein String nicht gleich ein String und ein Word ist nicht gleich ein Word....... Versuche ungefähr so zu arbeiten:
Delphi-Quellcode:
Sieht zwar sehr wild aus hilft aber bei Typinkompatibilitäten ungemein.
var
longintbuffer : array [0..3] of byte; gelesen:=Strom.Read(longintbuffer,sizeof(longintbuffer)); ... plongintbuffer:=@longintbuffer; .. myrecord.mylongint:=plongintbuffer^; Gruß K-H |
AW: Datei mit dynamischen Array einlesen
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,
Vielen Dank für all die Hilfestellungen und Denkanregungen, hab zwar das Problem noch immer nicht durchschaut, bin aber so glaube ich zumindest mal einen Schritt weiter... - Hab den Vorschlag mit ReadBuffer() umgesetzt - Beim Ausführen bekomme ich einen RunError 203, also einen Heap Overflow Anbei ist die shp-Datei als zip im Anhang, die ich versuche, einzulesen. Es handelt sich hier um ein ESRI-Shapefile, die Dokumentation zum Aufbau der Datei ist hier : ![]() Ich habe die Datei mit einem HexEditor durchleutet und durchgezählt, von der Datei her passt auch alles wie laut Dokumentation, beinhaltet eine PolyLinie mit 1 Part und 6 Points Werte ich allerdings mein Objekt ShapeFile aus zur Laufzeit, dann sehe ich, dass die Variablen NumParts und NumPoints irgendwelche unnachvollziehbare hohe Werte enthalten - deswegen wird anscheinend mein Array dann überverhältnismäßig groß dimensioniert und es kommt zu einem Heap Overflow. Hier die Auswertung: <TSHAPEFILE> = { <TOBJECT> = { _vptr$TOBJECT = $592b1c}, FILEHEADER = { FILECODE = 9994, UNUSED1 = 0, UNUSED2 = 0, UNUSED3 = 0, UNUSED4 = 0, UNUSED5 = 0, FILELEN = 126, VERSION = 1000, SHAPETYPE = 3, XMIN = 10, YMIN = 10, XMAX = 172.48123448885769, YMAX = 115.67794078524194, ZMIN = 0, ZMAX = 0, MMIN = 0, MMAX = 0}, RECORDHEADER = { NUMMER = 9280, LAENGE = 0}, PLHEAD = { SHAPETYPE = 1076101120, BOX = { 75, 20, 27.288444271758017, 66.00672307939206}, NUMPARTS = 411976715, NUMPOINTS = 1079176168}, PLCONTENT = { PARTS = $0, POINTS = $0}, ERROR = false, ERRMSG = $0, PUNKTE = $0, POLYLINIEN = $2baed0} ------------------ und hier der Quelltext meiner Unit (hatte gestern den Code nur ungefähr abgeschrieben):
Delphi-Quellcode:
unit shapefiles;
{$mode objfpc}{$H+} interface uses Classes, SysUtils; type TSHPFileHeader = packed record // Big Endian FileCode : LongInt; Unused1, Unused2, Unused3, Unused4, Unused5 : LongInt; FileLen : LongInt; // Little Endian Version : LongInt; ShapeType: LongInt; Xmin : Double; Ymin : Double; Xmax : Double; Ymax : Double; Zmin : Double; Zmax : Double; Mmin : Double; Mmax : Double; end; type TRecordHeader = packed record Nummer : LongInt; // Big Endian Laenge : LongInt; // Big Endian end; type TBox = Array[0..3] of Double; type TPunkt = packed record ID : LongInt; X : Double; Y : Double; end; type TPunktXY = record X, Y : Double; end; type TPolyLine = record ShapeType : LongInt; Box : TBox; NumParts : LongInt; NumPoints : LongInt; Parts : Array Of LongInt; Points : Array Of TPunktXY; end; type TPolyHeader = packed record Shapetype : LongInt; Box : TBox; NumParts : LongInt; NumPoints : LongInt; end; type TPolyContent = packed record Parts : Array of LongInt; Points : Array of TPunktXY; end; type TShapefile = class public FileHeader : TSHPFileHeader; RecordHeader : TRecordHeader; PLHead : TPolyHeader; PLContent : TPolyContent; error : boolean; errmsg : String; Punkte : Array Of TPunkt; PolyLinien : Array of TPolyLine; constructor Create; procedure AddPoint (ID : LongInt; X, Y : Double); procedure AddPLPart (ID : LongInt; zahl : LongInt); procedure LoadSHP(datnam : string); end; const shpNullShape = 0; shpPoint = 1; shpPolyLine = 3; shpPolygon = 5; shpMultiPoint = 8; shpPointZ = 11; shpPolyLineZ = 13; shpPolygonZ = 15; shpMultiPointZ = 18; shpPointM = 21; shpPolyLineM = 23; shpPolygonM = 25; shpMultiPointM = 28; shpMultiPatch = 31; sizeHeader = 100; procedure SwapBytes(Var Bytes; Len : Integer); implementation constructor TShapeFile.Create; begin inherited; error:=false; // Dynamische Arrays auf 0 setzen SetLength(Punkte,0); SetLength(PolyLinien,0); end; procedure TShapeFile.AddPoint(ID : LongInt; X, Y : Double); begin SetLength(Punkte,Length(Punkte)+1); Punkte[High(Punkte)].ID:=ID; Punkte[High(Punkte)].X:=X; Punkte[High(Punkte)].Y:=Y; end; procedure TShapeFile.AddPLPart (ID : LongInt; zahl : LongInt); begin //if (High(PolyLinien)<ID) then SetLength(PolyLinien,ID+1); // end; procedure TShapefile.LoadSHP(datnam : string); var Strom : TFileStream; In_Punkt : TPunkt; index : integer; i,j : integer; begin // Lies den Header des Shapefiles ein try Strom:=TFileStream.Create(datnam,fmOpenRead); Strom.ReadBuffer(FileHeader,SizeOf(FileHeader)); except error:=true; errmsg:='Kein gültiges ESRI-Shapefile!'; Strom.Free; exit; end; // Big Endian in Little Endian umwandeln SwapBytes(FileHeader.FileCode,SizeOf(FileHeader.FileCode)); SwapBytes(FileHeader.FileLen,SizeOf(FileHeader.FileLen)); // Records einlesen index:=0; try repeat index:=index+1; Strom.ReadBuffer(RecordHeader, SizeOf(RecordHeader)); // Big Endian SwapBytes(RecordHeader.Nummer, SizeOf(RecordHeader.Nummer)); SwapBytes(RecordHeader.Laenge, SizeOf(RecordHeader.Laenge)); case FileHeader.ShapeType of 1 : begin // Punkt - Shapefile Strom.Read(In_Punkt,SizeOf(In_Punkt)); AddPoint(RecordHeader.Nummer, In_Punkt.X, In_Punkt.Y); end; 3 : begin // PolyLine - Shapefile SetLength(PolyLinien,index); Strom.ReadBuffer(PLHead,SizeOf(PLHead)); SetLength(PLContent.Parts,PLHead.NumParts); SetLength(PLContent.Points,PLHead.NumPoints); Strom.ReadBuffer(PLContent,SizeOf(PLContent)); end; end; until (Strom.Position>=((FileHeader.FileLen)*2)); // da Filesize in Word = // 16-Bit = 2 Bytes angegeben wird except error:=true; errmsg:='Fehler beim Einlesen der einzelnen Elemente bei ID : '+IntToStr(RecordHeader.Nummer); Strom.Free; exit; end; Strom.Free; end; procedure SwapBytes(Var Bytes; Len : Integer); // Vertauscht die Byte-Order Var swapped : PChar; // Usage : SwapBytes(i,sizeof(i)); i : Integer; begin GetMem(swapped, Len); try for i:=0 to Len-1 do swapped[Len-i-1]:=PChar(@Bytes)[i]; Move(swapped^, Bytes, Len); finally FreeMem(swapped); end; end; end. |
AW: Datei mit dynamischen Array einlesen
RecordHeader.Nummer passt auch schon nicht sehe ich gerdae :?
|
AW: Datei mit dynamischen Array einlesen
Könntest du dir mal bitte angewöhnen, die Foren-Tags die dir hier zur verfügung gestellt werden, auch zu benutzen?
Das ist ja echt schrecklich, das kann doch keiner lesen. (Du kannst auch nachträglich noch, deine Beiträge editieren) |
AW: Datei mit dynamischen Array einlesen
Danke für den Hinweis, bin neu im Forum und wusste noch nicht, dass das geht --- sieht doch gleich besser aus :)
Glaube in der Zwischenzeit, dass bei mir irgendwo was verschoben sein muss, die ersten 100 Byte Fileheader passen ja auch, dann wirds haarig: ab Offset 100 steht in der Datei: 00 00 00 01 - ist Integer Big Endian, Wert sollte 1 sein da erster Record, danach kommt 00 00 00 48 - ebenfalls laut Doc Integer Big Endian, liefert die Größe des Records dann 03 00 00 00 - Little Endian, soll den Wert 3 liefern aber der erste Wert RecordHeader.Nummer stimmt schon nicht... Wer hat den Durchblick? |
AW: Datei mit dynamischen Array einlesen
Laut ESRI Doku sind manche Datenfelder Big Endian und andere sind Little Endian (
![]() :wall: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:40 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-2025 by Thomas Breitkreuz