![]() |
Binärdatei lesen und durchsuchen
Hallo zusammen,
ich hätte ja nicht gedacht, daß ich mal einzelne Bits würde schubsen müssen, aber jetzt ist es wirklich soweit. Ich muss CGM-Dateien nach Text durchsuchen. Zur Erklärung: "CGM" steht für "Computer Grafics Metafile" und ist ein offener Standard zum Austausch von CAD-Daten. Beschrieben ist das ganze in einer ISO-Norm, die es aber glücklicherweise zum Download gibt. Es gibt zwei Speicherformate für CGM, binär und ASCII. Die Textversion ist aber leider für mich nicht relevant, weil sich der Kunde aufgrund bereits vorhandener großer Datenbestände im Binärformat nicht auf ASCII-Codierung umstellen kann. Wie auch immer, das Binärformat ist so aufgebaut, daß jedes Grafikobjekt eine Elementklasse und eine Element-ID besitzt, über die sich die Art des Grafikelementes eindeutig bestimmen lässt. So ist beispielsweise Text immer mit der Klasse "4" und der ID "4" am Beginn der ersten 16 Bit des betreffenden Abschnittes zu identifizieren. Ein Beispiel (aus der Norm zitiert): Der Text "Hydrogen" an den Koordinaten x=0 und y=1
Code:
Nun ist es mir zwar schon gelungen, eine Datei zu öffnen und *irgend etwas* zu lesen, aber von irgendeiner Kontrolle ist dieses Experiment noch weit entfernt:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
| 4 | 4 | 15 | Header, Klasse 4 und ID 4, Länge der Parameterliste ist 15 | 0 | x=0 | 1 | y=1 | 8 | 'H' | Textlänge, String | 'y' | 'd' | String | 'r' | 'o' | String | 'g' | 'e' | String | 'n' | 0 | String, auffüllende Null
Code:
Wer kann hilfreiche Hinweise zum sinnvollen weiteren Vorgehen geben?
procedure TForm1.BtnGoClick(Sender: TObject);
var Datei: file; NumRead: Integer; Buf: Integer; begin if OpenDialog1.Execute then begin AssignFile(Datei, OpenDialog1.FileName); Reset(Datei, 1); Memo.Lines.Text := 'Dateigröße: ' + IntToStr(FileSize(Datei)) + ' Bytes'; repeat BlockRead(Datei, Buf, 2, NumRead); if Buf = 4 then begin Memo.Lines.Text := Memo.Lines.Text + ' | ' + IntToStr(Buf); end; until (NumRead = 0); CloseFile(Datei); end; end; Besten Dank! Torphyr |
Re: Binärdatei lesen und durchsuchen
bau dir records mit denen du die daten lesen kannst.
Delphi-Quellcode:
type rheader = packed record
klasseid: Byte; parameterlaenge: Byte; end; // für andere typen machst du dir einfach noch weitere records // welche du dann nach dem header einfach einliest. var h : rheader; fs : TFilestream; count : integer; s : String; begin fs := TFilestream.Create('c:\text.txt', fmOpenRead); try repeat count:=fs.Read(h, sizeof(rheader)); if count>0 then begin case h.klasseid of // andere typen hier einfügen $44 : begin SetLength(s, h.parameterlaenge); // hier den string lesen count:=fs.Read(s[1], h.parameterlaenge); MessageDlg(s, mtWarning, [mbOK], 0); end; end; end; until count=0; finally fs.free; end; end; |
Re: Binärdatei lesen und durchsuchen
Hi generic,
Klasse, Danke für die schnelle und gehaltvolle Antwort. Ich mach mich gleich ans Ausprobieren, habe aber an einer Stelle noch meine Zweifel: Zitat:
Wenn ich die Typen jetzt als volle Bytes definiere, schnippel ich doch den Header mittendurch, oder? :gruebel: Mit dem Editor zwischen den Zähnen, Torphyr |
Re: Binärdatei lesen und durchsuchen
Moin Torphyr,
erst einmal herzlich willkommen hier in der Delphi-PRAXiS. Was erhältst Du, wenn Du erst einmal nur den Header ausliest, und Dir diesen mal hexadezimal anzeigen lässt? (bezogen auf Deine Beispieldatei) Beispiel:
Delphi-Quellcode:
var
fsIN : TFileStream; wHeader : WORD; begin fsIN := TFileStream('PfadZuDerCGMDatei',fmOpenRead); try fsIN.Read(wHeader,2); ShowMessage(IntToHex(wHeader,4)); finally FreeAndNil(fsIN); end; end; |
Re: Binärdatei lesen und durchsuchen
Hallo Christian,
Danke für den Vorschlag, soweit klappt das schon mal. Bloß: Was ich da zu sehen bekomme ist natürlich nur der Header des ersten Wortes im Stream. Und wahrscheinlich stimmt der Wert nicht mal, weil ja die Bitfolge zuvor umgekehrt werden müsste (ich frage mich sowieso schon die ganze Zeit, warum die invertiert ist). Ich müsste also folgendes machen: 1. Den stream wortweise lesen. 2. Die Bitfolge des Wortes umkehren (von 15 ... 0 nach 0 ... 15) 3. Prüfen, ob die Bits 5 ... 11 und 12 ... 15 jeweils 4 ergeben 4. Wenn das der Fall ist, die weiteren Informationen auslesen (Position und Text) Nun bin ich inzwischen auf "TBits" gestoßen, kann aber nicht viel dazu finden, außer daß das ein Array of Bits mit beliebiger Länge ist. Keine Ahnung, ob ich damit was zuwege bringe ... Torphyr |
Interessante QuelleBin gerade
Bin gerade auf eine interessante Quelle zum Thema Byte-Manipulation gestoßen:
![]() Ist aber echt trockenes Brot :? Was soll's, ich hab ja gesunde Zähne! :mrgreen: UPDATE Halt, hier, noch besser: ![]() Ganz unten, bei "Klassenbibliotheken" der Link "Dlib". Ind der unit "ubits" steckt eine Menge tolles Zeug. Allmählich fasse ich wieder Mut :) |
Re: Binärdatei lesen und durchsuchen
Moin Torphyr,
mir ging's eigentlich mal darum, dass Du kurz ein Beispiel zeigst, aus dem man dann ableiten kann, wie die gelesenen Daten zu verarbeiten sind. Wenn ich das richtig sehe, ist ja nur das erste gelesene Word aufzubereiten, den Rest eines jeden Satzes kann man dann ja direkt auslesen, da hier nichts innerhalb eines Byte/Word codiert ist. (und ich wollte mir nicht extra die Doku zu CGM besorgen ;-) ) |
Re: Binärdatei lesen und durchsuchen
Liste der Anhänge anzeigen (Anzahl: 1)
So, das Gröbste ist geschafft. Noch mal zur Erinnerung: Ich wollte das Binär-CAD-Format CGM lesen, um die darin enthaltenen Texte und die jeweils zugehörigen X/Y-Koordinaten für weitere Verwendung zu extrahieren. Danke für die Anregungen! Nun bin selbst draufgekommen, wie's gemacht wird, auch wenn es eine Weile gedauert hat (und sicher nicht sehr elegant ist). Egal: Es tut. Einen Haken hat die Sache aber noch: Zwar komme ich jetzt an die Koordinaten, nicht jedoch aber zu einer vernünftigen Interpretation. Das scheint mir jedoch ein eigenes, eher mathematisches Problem zu sein, für das ich besser einen neuen Thread starte.
So, nun noch das kommentierte Listing:
Delphi-Quellcode:
Den Link zum weiterführenden Thread trage ich noch nach.
unit CGM;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; BtnGo: TButton; OpenDialog1: TOpenDialog; Memo2: TMemo; Label1: TLabel; procedure BtnGoClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} Function BTstBit ( Zahl : Byte; Bitnr : Integer ) : Boolean; (* -------------------------------------------------------------------- *) Begin Bitnr := Bitnr And $0007; Btstbit := (( Zahl Shr Bitnr ) And 1 ) = 1 End; Function BSetBit ( Zahl : Byte; Bitnr : Integer ) : Byte; (* -------------------------------------------------------------------- *) Var I : Byte; Begin Bitnr := Bitnr And $0007; I := 1; Bsetbit := Zahl Or ( I Shl Bitnr ) End; Function WSetBit ( Zahl : Word; Bitnr : Integer ) : Word; (* -------------------------------------------------------------------- *) Var I : Byte; Begin Bitnr := Bitnr And $000F; I := 1; Wsetbit := Zahl Or ( I Shl Bitnr ) End; Function ByteToBinStr ( Zahl : Byte ) : String; (* -------------------------------------------------------------------- *) Var I : Integer; W : String[8]; Begin W := '00000000'; For I := 7 Downto 0 Do If Btstbit ( Zahl,I ) Then Insert ('1',W,8 - I ); ByteToBinStr := W End; Function BinStrToByte ( Str1 : String ) : Byte; (* -------------------------------------------------------------------- *) Var I,J : Integer; K : Byte; Begin K := 0; J := Length ( Str1 ); If ( J > 8 ) Then J := 8; For I := J Downto 1 Do If ( Str1[i] = '1') Then K := Bsetbit ( K, J - I ); BinStrToByte := K End; Function BinStrToWord ( Str1 : String ) : Word; (* -------------------------------------------------------------------- *) Var I,J : Integer; W : Word; Begin W := 0; J := Length ( Str1 ); If ( J > 16 ) Then J := 16; For I := J Downto 1 Do If ( Str1[I] = '1') Then W := Wsetbit ( W, J - I ); BinStrToWord := W End; procedure TForm1.BtnGoClick(Sender: TObject); var fs : TFilestream; bHeader, ByteParam : Byte; WordParam : Word; boxb, boxh, posx, posy : Smallint; count, i, j, k, paramlist, len : Integer; bin, Byte01, Byte02, ausgabe, HClass, HID, PLLength, cgmtext, ding : String; begin // 1 if OpenDialog1.Execute then { Öffnen-Dialogfeld anzeigen } begin // 2 fs := TFileStream.Create(OpenDialog1.FileName,fmOpenRead); // Okay, wir lesen die Datei ein i := 0; // Zähler für die Grafik-Objekte in der Datei bin := ''; // Variable für Binärstrings ausgabe := ''; // Kommt ins linke Textfenster: Die Header der Grafik-Objekte cgmtext := ''; // Kommt ins rechte Textfenster: Der Text und (bisher leider noch nicht) seine Koordinaten memo1.Text := ''; // Textfenster ... memo2.Text := ''; // ... nullen try // 3 repeat // so lange wiederholen, bis die Datei alle ist :-) count:=fs.Read(bHeader, sizeof(bheader)); Byte01 := ByteToBinStr(bHeader); // Die ersten zwei Bytes enthalten den count:=fs.Read(bHeader, sizeof(bheader)); // Header. Wir wandelnb sie in einen Byte02 := ByteToBinStr(bHeader); // Binärstring ... i := i + 1; // Grafikobjekt Nr. bin := Byte01 + Byte02; // ... und kleben sie aneinander. HClass := ''; // CGM-Klasse HID := ''; // CGM - ID PLLength := ''; // Parameter-Lauflänge // Dann gucken wir uns den Header genauer an: For j := 1 to 16 do begin case j of 1..4: HClass := HClass + bin[j]; // Die ersten vier Bit stehen für die Klasse 5..11: HID := HID + bin[j]; // Sieben Bit für die ID (meinzeit, vier hätten es auch getan) 12..16: PLLength := PLLength + bin[j]; // Der Rest geht für die Lauflänge drauf end; end; HClass := IntToStr(BinStrToByte(HClass)); // Zurück zu etwas mehr lesbarem: Die CGM-Klasse ... HID := IntToStr(BinStrToByte(HID)); // ... die ID .. PLLength := IntToStr(BinStrToByte(PLLength)); // ... und die Lauflänge. // Bei Klasse 4 und ID 4 haben wir Text an einer X/Y-Position, alles andere // ist erstmal uninteressant. if (StrToInt(HClass) = 4) AND (StrToInt(HID) = 4) then begin // 4 paramlist := StrToInt(PLLength); cgmtext := cgmtext + '*** ' + IntToStr(i) + #13#10; // Die laufende Nummer des Grafik-Objektes for j := 1 to paramlist do // So oft, wie's Parameter gibt begin // 5 case j of // 6 1: begin // ??????????????????????? ding := ''; // Noch nicht entschlüsselt! fs.Read(ByteParam, sizeof(ByteParam)); // Als erstes kommt die X-Koordinate mit einer Byte01 := ByteToBinStr(ByteParam); // Lauflänge von vier Bits, relevant sind wahrscheinlich fs.Read(ByteParam, sizeof(ByteParam)); // die ersten zwei. Byte02 := ByteToBinStr(ByteParam); bin := Byte01 + Byte02; For k := 1 to 16 do begin ding := ding + bin[k]; end; cgmtext := cgmtext + 'Xbin: ' + ding + #13#10; cgmtext := cgmtext + 'X: ' + IntToStr(BinStrToWord(ding)) + #13#10; fs.Read(ByteParam, sizeof(ByteParam)); // Zwei Byte weiter in der Datei fs.Read(ByteParam, sizeof(ByteParam)); end; 2: begin fs.Read(ByteParam, sizeof(ByteParam)); // Als nächstes die Y-Koordinate, ... cgmtext := cgmtext + 'Y1: ' + ByteToBinStr(ByteParam) + #13#10; // ... zu Forschungszwecken diesmal .. fs.Read(ByteParam, sizeof(ByteParam)); // ... in Binärdarstellung (ich habe zum cgmtext := cgmtext + 'Y2: ' + ByteToBinStr(ByteParam) + #13#10; // Test CGM-Dateien mit X=Y fabriziert, die fs.Read(ByteParam, sizeof(ByteParam)); // Binärdartsellung der Y-Koordinate entspricht cgmtext := cgmtext + 'Y3: ' + ByteToBinStr(ByteParam) + #13#10; // also auch dem Wert für X). fs.Read(ByteParam, sizeof(ByteParam)); cgmtext := cgmtext + 'Y4: ' + ByteToBinStr(ByteParam) + #13#10; end; 3: begin fs.Read(WordParam, sizeof(WordParam)); cgmtext := cgmtext + 'Fin: ' + IntToStr(WordParam) + #13#10; // Abschlussflag. Uninteressant. end; 4: begin fs.Read(ByteParam, sizeof(ByteParam)); // Lauflänge des Textes. len := ByteParam; // Wird bis zum nächsten Word-Ende mit Nullen gefüllt. cgmtext := cgmtext + 'Len: ' + IntToStr(ByteParam) + #13#10 + 'Text: '; for k := 1 to len do // So lange es Zeichen gibt. begin fs.Read(ByteParam, sizeof(ByteParam)); if (ByteParam > 33) and (ByteParam < 126) then // Sicherung gegen ungültige Chars. cgmtext := cgmtext + char(ByteParam); end; if len / 2 = Int(len / 2) then // Eventuelles Füllbyte überspringen fs.Read(ByteParam, sizeof(ByteParam)); end; end; // 6 end; // 5 cgmtext := cgmtext + #13#10 + #13#10; // Leerzeile als Trennung zum nächsten Textobjekt end else begin // 4 paramlist := StrToInt(PLLength); // Wie oben (ja, ja, schon gut :-) ), Lauflänge der Parameter for j := 1 to paramlist do // Was jetzt kommt ist kein Text und ... fs.Read(ByteParam, sizeof(ByteParam)); // ... deshalb zu überspringen. end; // 4 // Kumulation der CGM-Objektliste für das linke Textfenster ausgabe := ausgabe + IntToStr(i) + #9 + '| ' + HClass + ' | ' + HID + ' | ' + PLLength + #13#10; Label1.Caption := IntToStr(count); // Nicht weiter wichtig ;-) until (count = 0); // Ende gut ... memo1.Text := ausgabe; // ... Programm tut ... memo2.Text := cgmtext; // ... Datenflut. finally FreeAndNil(fs); // Hier kommt keiner lebend raus! end; //3 end; // 2 end; // 1 end. [edit=Luckie]Code- durch Delphi-Tags ersetzt. Mfg, Luckie[/edit] |
Re: Binärdatei lesen und durchsuchen
Ich verstehe zwar nicht, worum es in dem QT letzten Endes geht, doch du solltest dich mal mit dem Borland Styleguide auseinandersetzen ;)
Und: Bei Delphi-QT bitte nicht die [ code]-, sonder die [ delphi]-Tags benutzen (ohne Leerzeichen). Dann klappt auch die Syntaxhervorhebung. Der Code ist in seiner jetzigen Form nicht so gut lesbar. Z.B. wechselst du zwischen "Begin" und "begin", manchmal
Code:
wobei oberes vorzuziehen wäre. Das hat den Grund, dass dann bei Verschachtelungen die "end;"'s nicht so krüppelig aussehen :)
for i := a to b do
begin //code end; // und manchmal: for i := a to b do begin //code end; \\edit: *gnarf* die Delphi-Tags formatieren schon selber ein wenig... also eben [ code]... Und der Kommentar zwischen Methodenkopf und dem begin ist *buäääää* :mrgreen: Ich mag jetzt völlig über-pingelig klingen, doch hab ich in den letzten Tagen sehr stark gemerkt, dass ein einheitliches Code-Bild nach einem Semi-Standard (Styleguide) ganz erheblich zur Lesbarkeit beiträgt! Wollte ich dir nur ans Herz gelegt haben *patsch* gruss, dizzy |
Re: Binärdatei lesen und durchsuchen
Hallo dizzy,
was willst Du, 'n Killer oder 'n Dressman? :-D Okay: Du hast Recht. Und ich keine Zeit, jedenfalls so lange es um Forschung geht. Später dann, naja ... Schüss, Torphyr |
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:55 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