TFileStream benutzen



  • Hallo Leute,

    ich habe bisher noch nie was mit TFileStream gemacht und das letzte Mal wo ich mit fopen & Co gearbeitet habe ist auch schon ein paar Jahre her.

    Mein naiver Versuch Daten aus einer Datei zu lesen ist auf jeden Fall fehlgschlagen.

    Nachdem Daten in den Buffer eingelesen wurden steht dort nur Müll drin - sprich der Müll der auch schon vorherdrin war (uninitialisiert) - d.h. er hat gar nichts eingelesen.

    Was mach ich falsch?

    Außerdem warum bekomme ich einen Fehler wenn ich mit unten abgedruckten Code
    zwei mal die gleich Datei öffne? Ich habe mit fmShareDenyWrite doch das Lesen zugelassen?

    void __fastcall TForm1::Button3Click(TObject *Sender)
    {
      TFileStream * File1 = NULL, * File2 = NULL;
      char buff1[100];
      char buff2[100];
    
      try
      {
        File1 = new TFileStream(Edit1->Text, fmOpenRead || fmShareDenyWrite);
        File2 = new TFileStream(Edit2->Text, fmOpenRead || fmShareDenyWrite);
    
        if(File1->Size != File2->Size)
          throw Exception("Die Dateien sind nicht identisch!");
    
        while(File1->Position < File1->Size)
        {
          int bytes_read;
    
          bytes_read = File1->Read(buff1, sizeof(buff1));
          File2->Read(buff2, sizeof(buff2));
    
          if(!CompareMem(buff1, buff2, sizeof(buff1)))
            throw Exception("Die Dateien sind nicht identisch!");
        }
      }
      __finally
      {
        delete(File1);
        delete(File2);
      }
    
      ShowMessage("Dateien sind identisch");
    }
    


  • Original erstellt von Brainchild:
    bytes_read = File1->Read(buff1, sizeof(buff1));

    Lass mich raten, bytes_read enthält nach dem Lesevorgang vermutlich 4?

    -junix



  • hallo,

    der zugriffsmode muß mit einem logischen oder verknüpft werden:

    falsch:
    File1 = new TFileStream(Edit1->Text, fmOpenRead || fmShareDenyWrite);
    File2 = new TFileStream(Edit2->Text, fmOpenRead || fmShareDenyWrite);
    
    richtig:
    File1 = new TFileStream(Edit1->Text, fmOpenRead | fmShareDenyWrite);
    File2 = new TFileStream(Edit2->Text, fmOpenRead | fmShareDenyWrite);
    

    außerdem würde ich zum prüfen auf gleichheit die klasse TMemoryStream heranziehen, da diese bereits eine für uns sehr günstige Methode namens "Memory" enthält. ich habe den code leicht modifiziert...

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
            TMemoryStream * File1 = NULL, * File2 = NULL;
            char buff1[100];
            char buff2[100];
    
            try
            {
                    File1 = new TMemoryStream();
                    File2 = new TMemoryStream();
                    File1->LoadFromFile(Edit1->Text);
                    File2->LoadFromFile(Edit2->Text);
    
                    if(File1->Size != File2->Size)
                        throw Exception("Die Dateien sind nicht identisch!");
    
                    if (File1->Size == File2->Size)
                        if (CompareMem(File1->Memory, File2->Memory, sizeof(File1)))
                                ShowMessage("identisch");
    
            }
            __finally
            {
                    delete(File1);
                    delete(File2);
            }
            ShowMessage("Dateien sind identisch");
    }
    

    der code sollte natürlich noch etwas verfeinert werden aber er funktioniert. dein alter code funktioniert zwar auch, aber das mittelstück ist nicht gerade optimal:

    void __fastcall TForm1::Button3Click(TObject *Sender)
    {
      TFileStream * File1 = NULL, * File2 = NULL;
      char buff1[100];
      char buff2[100];
    
      try
      {
        File1 = new TFileStream(Edit1->Text, fmOpenRead | fmShareDenyWrite); //logisches oder.
        File2 = new TFileStream(Edit2->Text, fmOpenRead | fmShareDenyWrite); //dto.
    
        if(File1->Size != File2->Size)
          throw Exception("Die Dateien sind nicht identisch!");
    
    /*
        while(File1->Position < File1->Size)
        {
          int bytes_read;
    
          bytes_read = File1->Read(buff1, sizeof(buff1));
          File2->Read(buff2, sizeof(buff2));
    
          if(!CompareMem(buff1, buff2, sizeof(buff1)))
            throw Exception("Die Dateien sind nicht identisch!");
        }*/ //da sinnlos
      }
      __finally
      {
        delete(File1);
        delete(File2);
      }
    
      ShowMessage("Dateien sind identisch");
    }
    

    mfg
    murph



  • Original erstellt von <murph>:
    **

    if (CompareMem(File1->Memory, File2->Memory, sizeof(File1)))
    

    **

    ...und dessen bist du dir sicher, ja?

    -junix



  • Original erstellt von junix:
    **Lass mich raten, bytes_read enthält nach dem Lesevorgang vermutlich 4?

    -junix**

    Ja, das sizeof in dem Fall nur die Größe des Pointers liefert ist mir auch schon aufgefallen. Mit festen Werten z.B. 100 hat es aber auch nicht geklappt - d.h. es wurde nichts in den Buffer gelesen.



  • Hmmmm initialisier mal dein array mit memset auf 0 und schau mal ob er was reinschreibt... und wie ist bei festen werten der Wert bytes_read? hast du <murph>'s Tip mit den flags schon umgesetzt?

    -junix



  • @murph

    1. Das verwirrt mich etwas, in meiner Hilfe steht "||" -> logisches OR. Und
    bei TFileStream wird laut Hilfe ein logischen OR zum Verknüpfen benötigt.
    Naja, so wie Du es sagt muss es dann aber wohl richtig sein, mit || Verknüpfen
    macht ja auch keinen Sinn.

    2. Mittlerweile bin ich auch schon auf die TMemoryStream Variante gekommen. Sie
    ist zu mindest einfach und funktioniert. Der Nachteil: Die Speicherauslastung
    geht ins Astromonische, wenn der Vergleich mit Großen Dateien durchgeführt
    wird. Deshalb wollte ich eigentlich häppchenweise lesen. Da ich hier nur ein
    Tool zur internen Verwendung schreibe dürfte das so schon OK sein, nur würde
    mich doch brennend interessieren wie man es richtig macht.

    Mein Code (getestet und funktionsfähig):

    TMemoryStream * File1 = new TMemoryStream();
      TMemoryStream * File2 = new TMemoryStream();
    
      try
      {
        File1->LoadFromFile(Edit1->Text);
        File2->LoadFromFile(Edit2->Text);
    
        if(File1->Size != File2->Size)
          throw Exception("Die Dateien sind unterschiedlich");
    
        if(!CompareMem(File1->Memory, File2->Memory, File1->Size))
          throw Exception("Die Dateien sind unterschiedlich");
      }
      __finally
      {
        delete(File1);
        delete(File2);
      }
    
      ShowMessage("Die Dateien sind identisch");
    


  • Ich hab's auch schon so probiert - ist aber nicht wirklich einfacher:

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
      FILE * file1, * file2;
      long file1_size, file2_size;
      char * buff1 = NULL, * buff2 = NULL;
    
      try
      {
        file1 = fopen(Edit1->Text.c_str(), "rb");
        file2 = fopen(Edit2->Text.c_str(), "rb");
    
        if((file1 == NULL) || (file2 == NULL))
          throw Exception("Eine der Dateien konnte nicht geöffnet werden.");
    
        file1_size = GetFileSize(file1);
        file2_size = GetFileSize(file2);
    
        if(file1_size != file2_size)
          throw Exception("Die Dateien sind nicht identisch");
    
        buff1 = new char [file1_size];
        buff2 = new char [file2_size];
    
        fread(buff1, sizeof(char), file2_size, file1);
        fread(buff2, sizeof(char), file2_size, file2);
    
        if(CompareMem(buff1, buff2, file1_size))
          ShowMessage("Dateien identisch");
        else
          ShowMessage("Dateien unterschiedlich");
      }
    
    long __fastcall TForm1::GetFileSize(FILE * file)
    {
       long curpos, length;
    
       curpos = ftell(file);
       fseek(file, 0L, SEEK_END);
       length = ftell(file);
       fseek(file, curpos, SEEK_SET);
       return(length);
    }
    


  • Original erstellt von junix:
    **Hmmmm initialisier mal dein array mit memset auf 0 und schau mal ob er was reinschreibt... und wie ist bei festen werten der Wert bytes_read? hast du <murph>'s Tip mit den flags schon umgesetzt?

    -junix**

    So ich hab jetzt jeweils ein memset drin. bytes_read liefert jetzt 0. Vorher war es machmal etwas mit 32148587234! TFileStream->Read ist zwar mit dem Rückgabewert int definiert, aber ich weis nicht was die Funktion wirklich zurückliefert. In Classes.pas steht folgendes:

    function THandleStream.Read(var Buffer; Count: Longint): Longint;
    begin
      Result := FileRead(FHandle, Buffer, Count);
      if Result = -1 then Result := 0;
    end;
    

    Die Methode FileRead habe ich jedoch niergends gefunden. In dem Buffer steht jetzt nach dem Aufruf von Read "" drin. Komisch find ich das schon.

    Ah!! Jetzt ja - eine Insel!!! Ich hatte noch das logische OR statt dem binären OR drin. Jetzt ließt er!

    Den von Borland sollte man mal die Hilfe um die Ohren schlagen! Grrr!!

    Ich probier mal aus ob ich den Rest jetzt auch noch hinbekomme...



  • So jetzt tut's:

    void __fastcall TForm1::Button3Click(TObject *Sender)
    {
      TFileStream * File1 = NULL, * File2 = NULL;
      char buff1[100], buff2[100];
    
      try
      {
        File1 = new TFileStream(Edit1->Text, fmOpenRead | fmShareDenyWrite);
        File2 = new TFileStream(Edit2->Text, fmOpenRead | fmShareDenyWrite);
    
        if(File1->Size != File2->Size)
          throw Exception("Die Dateien sind nicht identisch!");
    
        while(File1->Position < File1->Size)
        {
          int bytes_read;
    
          bytes_read = File1->Read(buff1, 100);
          File2->Read(buff2, 100);
    
          if(!CompareMem(buff1, buff2, bytes_read))
            throw Exception("Die Dateien sind nicht identisch!");
        }
      }
      __finally
      {
        delete(File1);
        delete(File2);
      }
    
      ShowMessage("Dateien sind identisch");
    }
    

    Kann mir zum Schluß noch jemand sagen wie ich aus dem Array die Anzahl der Elemente herausbekomme?


Anmelden zum Antworten