Dynamisches fgets in C

Joe1903

Mitglied
Hallo zusammen,

ich möchte bei folgendem Codesegment das fgets dynamisch machen, sprich Länge ist nicht bekannt und ohne statische Puffer.Ich habe gelesen,dass man das mit realloc machen kann,aber weil ich noch ein ziemlicher Neuling in C bin,weiss ich nicht,wie ich das angehen kann.
Kann mir jemand ein kleines Beispiel zeigen bitte?Vielen Dank im Voraus.

C++:
...

int ReadInputToTree(
 WORD **DestTree, 
 size_t *Treecount, 
 FILE *Input)
{
   int Status = SUCCESS;
   char Buf[8192] = {0};
   char *Word = NULL;

   while(fgets(Buf, sizeof Buf, Input) != NULL)
   {
      Word = strtok(Buf, NONALPHA);
      while(Status == SUCCESS&& Word != NULL)
      {
         Status = AddToTree(DestTree, Treecount, Word);

         if(Status == SUCCESS)
         {
           Word = strtok(NULL, NONALPHA);
         }
      }
   }

   return (Status);
}
...
 

Joe1903

Mitglied
Moin,

mit realloc kannst Du allokierten Speicher erneut in anderer Größe allokieren.
Ist allerdings ein gefährliches Spiel, sehr schnell fehlerhaft!
Ich würde es tunlichst vermeiden ...

Hier mal zwei Anregungen:
https://www.tutorialspoint.com/c_standard_library/c_function_realloc.htm
http://en.cppreference.com/w/c/memory/realloc

Gruß Klaus

Danke für deine Antwort.Wenn es so fehlerhaft ist,vermeide ich es.
Wie würdest du es dann dynamisch machen?Also ohne statischen Puffer lösen?
 

cwriter

Erfahrenes Mitglied
Danke für deine Antwort.Wenn es so fehlerhaft ist,vermeide ich es.
Realloc funktioniert gut - die Frage ist nur, wie gut man damit umgehen kann.

Wie würdest du es dann dynamisch machen?Also ohne statischen Puffer lösen?
Lies in einen statischen Puffer. Dann überprüfst du, ob ein Zeilenumbruch vorkommt oder fgets() NULL zurückgibt.
Wenn das 2. letzte Zeichen kein LF ist, lies weiter, bis ein LF vorkommt. Denke aber daran, dieses Byte auf 0 zu setzen, um nicht ewig zu loopen.

Gruss
cwriter
 

Joe1903

Mitglied
Frage ist nur, wie gut man damit umgehen kann.

Ich habe mal das gemacht.Wo sind hier meine Fehler?

C++:
...
int ReadInputToTree(
 WORD **DestTree,
 size_t *Treecount,
 FILE *Input)
{
   int Status = SUCCESS;
   char Buf[8192] = {0};
   char *Word = NULL;
   int inputLen;

 inputLen = sizeof(&Input);

 if (Buf == NULL)
 {
  exit(1);
 }

while(fgets(Buf, sizeof Buf, Input))
{
  Buf = (char *) realloc(Buf, sizeof(char) * inputLen);
  if (Buf == Null)
  {
   exit(1);
  }
  else
  {
       Word = strtok(Buf, NONALPHA);
       while(Status == SUCCESS && Word != NULL)
       {
          Status = AddToTree(DestTree, Treecount, Word);

         if(Status == SUCCESS)
         {
           Word = strtok(NULL, NONALPHA);
         }
      }
   }

 free(Buf);

 return (Status);
}
...
 
Zuletzt bearbeitet:

cwriter

Erfahrenes Mitglied
Du vermischst Stack und Heap.
char a[123] ist auf dem Stack. *alloc ist aber eine Heapfunktion. Du kannst nicht Stack (de-)allokieren, nur pushen und poppen.

Wenn du char Buf[8196]; auf char* Buf = NULL; änderst und den nachfolgenden Test löschst, sollte es gehen.

Allerdings ist den Code sonst ziemlich inkorrekt. sizeof(&Input) ist immer 32 bzw. 64 bit. Dann setzt du also einen char* auf einen Wert. Warum? Du willst ja eine Länge, keine Adresse... Wenn du tatsächlich eine Datei hast, kannst du die Grösse mit fseek(f, 0, SEEK_END); len=ftell(f); herausfinden. Das geht NICHT bei stdin. Dann reicht aber auch ein malloc() statt eines realloc(), sofern du genug RAM übrig hast - ansonsten reicht auch mein statischer Vorschlag.

Gruss
cwriter

PS: C-Pointer-Arithmetik ist lustig, aber du übertreibst es ein bisschen mit Pointern und Referenzierungen...
 

Joe1903

Mitglied
Du vermischst Stack und Heap.
char a[123] ist auf dem Stack. *alloc ist aber eine Heapfunktion. Du kannst nicht Stack (de-)allokieren, nur pushen und poppen.

Wenn du char Buf[8196]; auf char* Buf = NULL; änderst und den nachfolgenden Test löschst, sollte es gehen.

Allerdings ist den Code sonst ziemlich inkorrekt. sizeof(&Input) ist immer 32 bzw. 64 bit. Dann setzt du also einen char* auf einen Wert. Warum? Du willst ja eine Länge, keine Adresse... Wenn du tatsächlich eine Datei hast, kannst du die Grösse mit fseek(f, 0, SEEK_END); len=ftell(f); herausfinden. Das geht NICHT bei stdin. Dann reicht aber auch ein malloc() statt eines realloc(), sofern du genug RAM übrig hast - ansonsten reicht auch mein statischer Vorschlag.

Gruss
cwriter

PS: C-Pointer-Arithmetik ist lustig, aber du übertreibst es ein bisschen mit Pointern und Referenzierungen...

Könntest du mir bitte einen Teil des Codes schreiben?Ich muss das mal sehen. Ich benutze es auch nicht versprochen.
 

cwriter

Erfahrenes Mitglied

Joe1903

Mitglied
Kann ich, aber du müsstest dich bis etwa 21 Uhr gedulden. Erst dann habe ich wieder eine anständige Tastatur :)

Das wäre mir - ehrlich gesagt - ziemlich schnuppe :).
Aber gute Einstellung ;)

Einen Teil zu schreiben ist allerdings recht schwierig. Ich kann ja mal den Rohbau versuchen.

Gruss
cwriter

Vielen Dank.Hier ist der ganze Code(nicht von mir). Die betroffene Funktion ist int ReadInputToTree(WORD **DestTree, size_t *Treecount, FILE *Input).Ich habe jetzt mal angeapsst,aber es funktioniert noch immer nicht.
Zeit ist egal,Hauptsache ich seh mal wie man das macht :)

C++:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>


typedef struct WORD
{
   char *Word;
   size_t Count;
   struct WORD *Left;
   struct WORD *Right;
} WORD;


#define SUCCESS                      0
#define CANNOT_MALLOC_WORDARRAY      1
#define NO_WORDS_ON_INPUT            2
#define NO_MEMORY_FOR_WORDNODE       3
#define NO_MEMORY_FOR_WORD           4


#define NONALPHA "1234567890 \v\f\n\t\r+=-*/\\,.;:'#~?<>|{}[]`!\".$%^&()"


int ReadInputToTree(
 WORD **DestTree,
 size_t *Treecount,
 FILE *Input);

int AddToTree(
 WORD **DestTree,
 size_t *Treecount,
 char *Word);

int WalkTree(
 WORD **DestArray,
 WORD *Word);

int CompareCounts(
 const void *vWord1,
 const void *vWord2);

int OutputWords(
 FILE *Dest,
 size_t Count,
 WORD **WordArray);

void FreeTree(
 WORD *W);

char *dupstr(
 char *s);



main()
{
  int Status = SUCCESS;
   WORD *Words = NULL;
   size_t Treecount = 0;
   WORD **WordArray = NULL;

   if(Status == SUCCESS)
   {
      Status = ReadInputToTree(&Words, &Treecount, stdin);
   }

   if(Status == SUCCESS)
   {
      if(0 == Treecount)
      {
         Status = NO_WORDS_ON_INPUT;
      }
   }

   if(Status == SUCCESS)
   {
      WordArray = malloc(Treecount * sizeof *WordArray);
      if(WordArray == NULL)
      {
         Status = CANNOT_MALLOC_WORDARRAY;
      }
   }

   if(Status == SUCCESS)
   {
      Status = WalkTree(WordArray, Words);
   }

   if(Status == SUCCESS)
   {
      qsort(WordArray, Treecount, sizeof *WordArray, CompareCounts);
   }

   if(Status == SUCCESS)
   {
      Status = OutputWords(stdout, Treecount, WordArray);
   }

   if(WordArray != NULL)
   {
      free(WordArray);
      WordArray = NULL;
   }

   if(Words != NULL)
   {
      FreeTree(Words);
      Words = NULL;
   }

   if(Status != SUCCESS)
   {
      fprintf(stderr, "Program failed with code %d\n", Status);
   }
   return (SUCCESS == Status ? EXIT_SUCCESS : EXIT_FAILURE);
}


void FreeTree(
 WORD *W)
{
   if(W != NULL)
   {
      if(W->Word != NULL)
      {
         free(W->Word);
         W->Word = NULL;
      }
      if(W->Left != NULL)
      {
         FreeTree(W->Left);
         W->Left = NULL;
      }
      if(W->Right != NULL)
      {
         FreeTree(W->Right);
         W->Right = NULL;
      }
   }
}


int AddToTree(
 WORD **DestTree,
 size_t *Treecount,
 char *Word)
{
   int Status = SUCCESS;
   int CompResult = 0;

   if(*DestTree == NULL)
   {
      *DestTree = malloc(sizeof **DestTree);
      if(*DestTree == NULL)
      {
         Status = NO_MEMORY_FOR_WORDNODE;
      }
      else
      {
         (*DestTree)->Left = NULL;
         (*DestTree)->Right = NULL;
         (*DestTree)->Count = 1;
         (*DestTree)->Word = dupstr(Word);
         if((*DestTree)->Word == NULL)
         {
           Status = NO_MEMORY_FOR_WORD;
           free(*DestTree);
           *DestTree = NULL;
         }
         else
         {
           ++(*Treecount);
         }
      }
   }
   else
   {
      CompResult = strcmp(Word, (*DestTree)->Word);
      if(0 < CompResult)
      {
         Status = AddToTree(&(*DestTree)->Left, Treecount, Word);
      }
      else if(0 > CompResult)
      {
         Status = AddToTree(&(*DestTree)->Left, Treecount, Word);
      }
      else
      {
         ++(*DestTree)->Count;
      }
   }

   return (Status);
}


int ReadInputToTree(
        WORD **DestTree,
        size_t *Treecount,
        FILE *Input)
{
        int Status = SUCCESS;
        char *Buf = NULL;
        char *Word = NULL;
        int inputLen = sizeof(Input);

        while(fgets(Buf, sizeof(Buf), Input))
        {
                Buf = malloc(inputLen);
                Word = strtok(Buf, NONALPHA);
                while(Status == SUCCESS && Word != NULL)
                {
                        Status = AddToTree(DestTree, Treecount, Word);

                        if(Status == SUCCESS)
                        {
                                Word = strtok(NULL, NONALPHA);
                        }
                }
        }

        return (Status);
}



int WalkTree(
 WORD **DestArray,
 WORD *Word)
{
   int Status = SUCCESS;
   static WORD **Write = NULL;

   if(DestArray != NULL)
   {
      Write = DestArray;
   }

   if(Word != NULL)
   {
      *Write = Word;
      ++(Write);

      if(Word->Left != NULL)
      {
         Status = WalkTree(NULL, Word->Left);
      }
      if(Word->Right != NULL)
      {
         Status = WalkTree(NULL, Word->Right);
      }
   }

   return (Status);
}


int CompareCounts(
 const void *vWord1,
 const void *vWord2)
{
   int Result = 0;
   WORD * const *Word1 = vWord1;
   WORD * const *Word2 = vWord2;

   if((*Word1)->Count < (*Word2)->Count)
   {
      Result = 1;
   }
   else if((*Word1)->Count > (*Word2)->Count)
   {
      Result = -1;
   }
   else
   {
      Result = 0;
   }

   return (Result);
}


int OutputWords(
 FILE *Dest,
 size_t Count,
 WORD **WordArray)
{
   int Status = SUCCESS;
   size_t Pos = 0;

   fprintf(Dest, "Total Words : %lu\n", (unsigned long)Count);

   while(Status == SUCCESS && Pos < Count)
   {
      fprintf(Dest, "%10lu %s\n", (unsigned long)WordArray[Pos]->Count, WordArray[Pos]->Word);
      ++(Pos);
   }

   return (Status);
}


char *dupstr(
 char *s)
{
   char *Result = NULL;
   size_t slen = 0;

   slen = strlen(s);

   Result = malloc(slen + 1);

   if(NULL != Result)
   {
      memcpy(Result, s, slen);
      *(Result + slen) = '\0';
   }

   return (Result);
}
 

cwriter

Erfahrenes Mitglied
Vielen Dank.Hier ist der ganze Code(nicht von mir).
Spricht für dich...

C++:
int ReadInputToTree(
    WORD **DestTree,
    size_t *Treecount,
    FILE *Input)
{
    int Status = SUCCESS;
    char *Buf = NULL;
    size_t bufsize = 0;
    char *Word = NULL;
   
    //Ein bisschen fseek/ftell
    if (Input != stdin) //Nicht wirklich korrekt... (Gibt mehrere Dateien, die nicht mit ftell funktionieren)
    {
        size_t tmp = ftell(Input);    //Wo sind wir momentan?
        fseek(Input, 0, SEEK_END);    //Gehe ans Ende der Datei
        bufsize = ftell(Input);        //Wo sind wir jetzt?
        fseek(Input, tmp, SEEK_SET);    //Gehe zurück, wo wir herkamen


        //Hole die gesamte Datei in den Speicher
        Buf = (char*)malloc(bufsize);
        if (Buf == NULL) return -1;

        if (fread(Buf, sizeof(char), bufsize, Input) != bufsize) printf("Weird: The promised amount was not read...\n");
       
       
    }
    else
    {
        //Sonst gehe halt manuell vor:
        bufsize = 4;    //Hier extra klein, um den Effekt zu demonstrieren. Normalerweise nimmt man direkt 256 oder mehr.
        Buf = calloc(bufsize, sizeof(char));
        if (Buf == NULL) return -1;

        char* bufbase = Buf;    //Setze die Basis. Mit der arbeiten wir, an die Adresse der anderen schreiben wir.
       
        printf("bufb: %p\nBuf : %p\n", bufbase, Buf);

        while (fgets(Buf, (int)(bufsize - (Buf - bufbase)), Input) != NULL)
        {
            Buf += (int)(bufsize - (Buf - bufbase)) -1;    //Setze den neuen Index

            if (bufbase[bufsize - 2] != 0) {            //Only if something has changed (enough was read...)
                //We read everything, still no newline
                char* tmp = (char*)realloc(bufbase, bufsize * 2);
                if (tmp == NULL) {
                    printf("Failed to realloc\n");
                    free(bufbase);
                    return -1;
                }
                Buf = tmp + (Buf - bufbase);    //Keep the offset
                bufbase = tmp;
                bufsize *= 2;
                //Set the checker value again
                bufbase[bufsize - 2] = 0;
            }
            else break;

        }
        Buf = bufbase;    //Setze zurück.
    }

    Word = strtok(Buf, NONALPHA);
    while (Status == SUCCESS && Word != NULL)
    {
        Status = AddToTree(DestTree, Treecount, Word);

        if (Status == SUCCESS)
        {
            Word = strtok(NULL, NONALPHA);
        }
    }
   
    free(Buf);
    return (Status);
}

Deine Fehler lagen hauptsächlich am sizeof. Dieses sollte man NIE auf Pointer anwenden, denn sizeof(void*) == sizeof(char*) == sizeof(int*) == sizeof(FILE*)...
Der einzige Grund, es anzuwenden, ist bei Arrays von Pointern. Ansonsten: Nie. (Faustregel).
Pointer sind immer gleich gross. Sizeof funktioniert auch nicht bei Strings, dafür gibt es strlen() (was aber langsamer ist als sizeof, da es zur Runtime durchgeht (sizeof ist compiletime)).

Zuerst alles in einen String zu lesen, dann strtok anzuwenden, dann die einzelnen Teile des Strings zu kopieren und dann den Urstring zu löschen ist aber schon arg nah an dem, was man gemeinhin böse Worte schimpft. Ein einfaches scanf("%c") wäre dafür schon fast besser (scanf ist lahm, aber was da gemacht wird, ist schlimmer). Auch getc() wäre dafür eine Möglichkeit... Aber naja. Hast ja nicht du verbrochen.

Zurück zum Thema: Sollte soweit funktionieren, habe es auch getestet. Bei Fragen einfach fragen.

Gruss
cwriter