Gepuffertes Lesen einer binären Datei



  • Hallo,

    die Aufgabenstellung ist folgende:
    Eine Funktion zum gepuffertem Lesen einer binären Datei schreiben,
    die Funktion read_buffered soll einen static char Puffer der Größe 5000 besitzen.
    Jedes mal wenn die Funktion aufgerufen wird, soll sie, falls noch im Puffer, dessen Inhalt zurück geben, falls nicht einen neuen read() Aufruf ausführen, der die nächsten 5000 Bytes in den Puffer einließt.
    Ich verwende ein Linux-Betriebssystem und den gcc Compiler.

    Mein bisheriger Code:

    readbuf.h

    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <time.h>
    #include <sys/time.h>
    #include <string.h>
    
    int read_buffered(int fd, void *buf, int nbytes);
    

    readbuf.c

    #include "readbuf.h"
    
    typedef struct{
        int size;
        int bytes_to_read;
    } state;
    
    int read_buffered(int fd, void *buf, int nbytes)
    {
        static char sysbuf[5000]; //Systempuffer, 5000 Bytes lang
        void* mySysBuf = sysbuf;
        state myState;// Zustandsinformation zum Systempuffer
        static int bytes_copied = 0;  // Anzahl Bytes, die während dieses Aufrufs schon von sysbuf nach buf kopiert wurden
        myState.bytes_to_read = nbytes;
        myState.size = 0;
    
        while((bytes_copied < nbytes))
        {
            /*Kopiere weitere Bytes von sysbuf nach buf
             * wenn keine mehr verfügbar: lese mit read() neue Bytes ein nach sysbuf
             * */
    
            if((myState.size == 0))
            {
                int myRead = read(fd, mySysBuf, 5000); //Lese nächste 5000 Bytes
                myState.size = myRead;
                printf("Filling Buffer, read-value: %i\n", myRead);
            }
    
            if(myState.bytes_to_read <= 5000 - (bytes_copied % 5000))
            {
                memcpy(buf + bytes_copied, mySysBuf + (bytes_copied % 5000), myState.bytes_to_read);
                bytes_copied += myState.bytes_to_read; // in diesem Schleifenaufruf kopierte Bytes
                myState.size -= myState.bytes_to_read;
                myState.bytes_to_read = 0;
                printf("Copying last Bytes\n");
            }
            else
            {
                memcpy(buf + bytes_copied, mySysBuf, 5000 - (bytes_copied % 5000));
                bytes_copied += 5000 - (bytes_copied % 5000); // in diesem Schleifenaufruf kopierte Bytes
                myState.size = 0;
                myState.bytes_to_read -= 5000 - (bytes_copied % 5000);
                printf("%i Bytes copied\n", 5000 - (bytes_copied % 5000));
            }
    
            printf("Size sysbuf: %i\nbytes to read: %i\nbytes copied: %i\nnbytes: %i\n", myState.size, myState.bytes_to_read, bytes_copied, nbytes);
            /* Aktualisiere Zustandsinformationvon sysbuf (wie voll ist sysbuf, welche Bytes noch ungelesen)*/
    
        }// END WHILE
        printf("read buffered ausgefuehrt!\n");
        getchar();
        return bytes_copied;
    }//END read_buffered
    

    In der main Funktion wird das Lesen angewendet und überprüft, jedoch funktioniert sie noch nicht.
    Bitte um Hilfe, vielen Dank!


  • Mod

    Was bedeutet "funktioniert nicht"? Gib ein vollständiges Programm mit einer vollständigen Problembeschreibung.

    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <time.h>
    #include <sys/time.h>
    #include <string.h>
     
     
     
    int read_buffered(int fd, void *buf, int nbytes);
    

    Es hat zwar nichts mit deinem Problem zu tun, aber dieser Header sollte lieber so aussehen:

    int read_buffered(int fd, void *buf, int nbytes);
    

    und dann die benötigten Header in der .c-Datei einbinden. Wenn ich das recht sehe, benötigst du von all diesen Headern ohnehin nur unistd.h, string.h und stdio.h (und letzteres nur für deine Debugausgaben).


  • Mod

    Vermutlich verhedderst du dich irgendwo in deinen vielen Variablen und Sonderfällen. Du scheinst in deinem Programm zwar schon die wichtigen Fälle identifiziert zu haben. Aber einige davon kann man noch auf einen allgemeinen Fall zurück führen. Ich würde nur die Fälle "wir haben genug" und "wir haben nicht genug" behandeln. Bei der Abhandlung von "wir haben nicht genug" noch eine kleine Nebenbehandlung des Sonderfalls "es ist gibt nichts mehr zu holen". Das sollte reichen. So sähe das aus:

    #include <unistd.h>
    ssize_t read_buffered(int fd, void *buf, size_t count);
    
    #include <string.h>
    
    ssize_t read_buffered(int fd, void *buf, size_t count)
    {
      // count: wie viel wir noch zu schreiben haben, falls möglich
    
      static struct
      {
        char buffer[5000];
        size_t begin, end;  // Anfang und Ende des genutzten Puffers
        int fd;             // Datei, die mit dem Puffer assoziiert ist
      } state; 
    
      if (fd != state.fd)  // Damit wir auch wirklich aus der richtigen Datei lesen
        {
          state.fd = fd;
          state.begin = 0;
          state.end = 0;
        }
    
      ssize_t written = 0;  // Wie viel wir schon nach buf geschrieben haben
      while(state.end - state.begin < count)  // So lange wir nicht genug im Puffer haben
        {
          // Schreibe alles, was noch im Puffer ist:
          memcpy(buf, state.buffer + state.begin, state.end - state.begin); 
          count -= state.end - state.begin;
          written += state.end - state.begin;
    
          // Versuche 5000 weitere Bytes zu lesen:
          {
            ssize_t bytes_read = read(fd, state.buffer, 5000);
            state.begin = 0;
            state.end = bytes_read;
            if (bytes_read <= 0) // Falls Lesefehler (aka Dateiende)
              {
                state.end = 0;
                return written;        
              }
          }
        }
      // Wenn wir hier ankommen, sind genug Daten im Puffer. Schreibe die Restzahl raus:
      memcpy(buf, state.buffer + state.begin, count);
      state.begin += count;
      written += count;    
      return written;
    }
    
    #include <fcntl.h>
    #include <stdio.h>
    int main()
    {
      int fd = open("test.c", O_RDONLY);
      if (fd)
        {
          char buf[20];
          ssize_t read_bytes;
          while ((read_bytes = read_buffered(fd, buf, 20)) > 0)
            {
              fwrite(buf, 1, read_bytes, stdout);
            }
        }
      else
        puts("File not opened");
      close(fd);
      return 0;
    }
    

    Insgesamt ist die Aufgabenstellung natürlich schrecklich, insofern hier ein Status innerhalb einer Funktion gespeichert wird, aber nicht garantiert ist, ob die Funktion überhaupt mit den selben Dateien aufgerufen wird. Ich versuche, dies halbwegs abzufangen, indem ich diesen Fall prüfe. Falls die Datei vom Nutzer gewechselt wird, gehen natürlich trotzdem eventuell Daten verloren, aber wir bekommen wenigstens keine falschen Ergebnisse. Besser geht es wohl kaum bei der Vorgabe. So ein Status gehört eigentlich außerhalb gespeichert.



  • Asam95 schrieb:

    Ich verwende ein Linux-Betriebssystem und den gcc Compiler.

    open(), unistd.h und Konsorten sind kein Standard-C.
    Bist du dir sicher, dass du damit nicht dein Thema verfehlst?



  • Die restlichen Bibliotheken die im Header eingebunden sind benötige ich noch zusätzlich in meiner main-Funktion, in welchem ich die Funktion read_buffered teste.
    Vielen Dank für die schnelle Hilfe 🙂 👍


  • Mod

    Asam95 schrieb:

    Die restlichen Bibliotheken die im Header eingebunden sind benötige ich noch zusätzlich in meiner main-Funktion, in welchem ich die Funktion read_buffered teste.

    Dann gehören die in die main-Funktion. In den Header kommt nur das, was unbedingt nötig ist.



  • Hallo, habe es mal probiert,

    readbuf.h:

    int read_buffered(int fd, void *buf, int nbytes);
    

    readbuf.c:

    #include "readbuf.h"
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    
    int read_buffered(int fd, void *buf, int nbytes)
    {
        static struct      // Zustandsinformation zum Systempuffer
        {
          char sysbuf[5000];
          int begin,end
        }state;
        int bytes_copied = 0;
    
                /*Kopiere weitere Bytes von sysbuf nach buf
             * wenn keine mehr verfügbar: lese mit read() neue Bytes ein nach sysbuf
             * */
    
        while((state.end - state.begin) < nbytes) //Lese weitere Bytes
        {
    	memcpy(buf, (state.sysbuf + state.begin), (state.end - state.begin)); //Restliche Bytes des Puffers kopieren
            nbytes -= state.end - state.begin;
    	bytes_copied += state.end - state.begin;
    
            int myRead = read(fd, state.sysbuf, 5000);
    	if(myRead == -1)
    	{
    	  perror("Fehler bei read");
    	  return myRead;
    	}
    	state.begin = 0;   //Aktueller Zustand zurücksetzen
    	state.end = myRead;
        }// END WHILE
        memcpy(buf, state.sysbuf, nbytes); //restliche Bytes kopieren
        state.begin += nbytes;
        bytes_copied += nbytes;
        return bytes_copied;
    }//END read_buffered
    

    Die while-Schleife läuft jedoch immer weiter, sie wird also nicht beendet, sobald die Bedingung zutrifft.

    Vielen Dank!


  • Mod

    Asam95 schrieb:

    Die while-Schleife läuft jedoch immer weiter, sie wird also nicht beendet, sobald die Bedingung zutrifft.

    while-Schleifen laufen ja auch solange eine Bedingung zutrifft. Was aber nicht dein Problem ist. Geh mal mit dem Debugger im Einzelschritt durch und vergleich das, was tatsächlich passiert, mit dem, wie du es dir vorgestellt hast. Falls das zu keiner Lösung führt, kannst du dir ja noch ein paar mehr Anregungen holen aus meinem Programm.


Log in to reply