AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Binärdatei lesen und durchsuchen

Offene Frage von "Torphyr"
Ein Thema von Torphyr · begonnen am 13. Mai 2004 · letzter Beitrag vom 28. Mai 2004
Antwort Antwort
Seite 1 von 2  1 2      
Torphyr

Registriert seit: 7. Nov 2003
Ort: Filderstadt
8 Beiträge
 
#1

Binärdatei lesen und durchsuchen

  Alt 13. Mai 2004, 10:54
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:
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
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:

Code:
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;
Wer kann hilfreiche Hinweise zum sinnvollen weiteren Vorgehen geben?

Besten Dank!
Torphyr
  Mit Zitat antworten Zitat
generic

Registriert seit: 24. Mär 2004
Ort: bei Hannover
2.416 Beiträge
 
Delphi XE5 Professional
 
#2

Re: Binärdatei lesen und durchsuchen

  Alt 13. Mai 2004, 15:15
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;
  Mit Zitat antworten Zitat
Torphyr

Registriert seit: 7. Nov 2003
Ort: Filderstadt
8 Beiträge
 
#3

Re: Binärdatei lesen und durchsuchen

  Alt 13. Mai 2004, 16:00
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 von generic:

Delphi-Quellcode:
type rheader = packed record
       klasseid: Byte;
       parameterlaenge: Byte;
     end;
Kann das gutgehen? Das Problem ist doch, daß im Header die Klasse, die ID und die Parameterlänge ungleichmäßig auf die zwei Byte verteilt sind (und obendrein auch noch rückwärts geschrieben werden, Bitfolge 15 ... 0). Die Klasse ist also nur 4 Bit breit, die ID folgt mit 7 Bit Breite, hängt also rüber ins zweite Byte, und dann kommen schließlich nochmal 5 Bit für die Länge der Parameterliste.
Wenn ich die Typen jetzt als volle Bytes definiere, schnippel ich doch den Header mittendurch, oder?

Mit dem Editor zwischen den Zähnen,
Torphyr
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.117 Beiträge
 
Delphi 11 Alexandria
 
#4

Re: Binärdatei lesen und durchsuchen

  Alt 13. Mai 2004, 18:31
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;
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Torphyr

Registriert seit: 7. Nov 2003
Ort: Filderstadt
8 Beiträge
 
#5

Re: Binärdatei lesen und durchsuchen

  Alt 14. Mai 2004, 11:55
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
  Mit Zitat antworten Zitat
Torphyr

Registriert seit: 7. Nov 2003
Ort: Filderstadt
8 Beiträge
 
#6

Interessante QuelleBin gerade

  Alt 14. Mai 2004, 13:41
Bin gerade auf eine interessante Quelle zum Thema Byte-Manipulation gestoßen:
http://www.merlyn.demon.co.uk/del-bits.htm#View
Ist aber echt trockenes Brot
Was soll's, ich hab ja gesunde Zähne!

UPDATE
Halt, hier, noch besser:
http://www.oszhdl.be.schule.de/gymna...load/index.htm
Ganz unten, bei "Klassenbibliotheken" der Link "Dlib". Ind der unit "ubits" steckt eine Menge tolles Zeug.
Allmählich fasse ich wieder Mut
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.117 Beiträge
 
Delphi 11 Alexandria
 
#7

Re: Binärdatei lesen und durchsuchen

  Alt 14. Mai 2004, 16:45
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 )
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Torphyr

Registriert seit: 7. Nov 2003
Ort: Filderstadt
8 Beiträge
 
#8

Re: Binärdatei lesen und durchsuchen

  Alt 28. Mai 2004, 16:25
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:
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.
Den Link zum weiterführenden Thread trage ich noch nach.

[edit=Luckie]Code- durch Delphi-Tags ersetzt. Mfg, Luckie[/edit]
Angehängte Dateien
Dateityp: zip multibin.zip (6,3 KB, 16x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von dizzy
dizzy

Registriert seit: 26. Nov 2003
Ort: Lünen
1.932 Beiträge
 
Delphi 7 Enterprise
 
#9

Re: Binärdatei lesen und durchsuchen

  Alt 28. Mai 2004, 16:33
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:
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]...
wobei oberes vorzuziehen wäre. Das hat den Grund, dass dann bei Verschachtelungen die "end;"'s nicht so krüppelig aussehen
Und der Kommentar zwischen Methodenkopf und dem begin ist *buäääää*


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
Fabian K.
INSERT INTO HandVonFreundin SELECT * FROM Himmel
  Mit Zitat antworten Zitat
Torphyr

Registriert seit: 7. Nov 2003
Ort: Filderstadt
8 Beiträge
 
#10

Re: Binärdatei lesen und durchsuchen

  Alt 28. Mai 2004, 16:38
Hallo dizzy,
was willst Du, 'n Killer oder 'n Dressman?

Okay: Du hast Recht.
Und ich keine Zeit, jedenfalls so lange es um Forschung geht. Später dann, naja ...

Schüss,
Torphyr
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:29 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz