# Dynamisches fgets in C



## Joe1903 (12. Dezember 2016)

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.


```
...

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);
}
...
```


----------



## vfl_freak (12. Dezember 2016)

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


----------



## Joe1903 (12. Dezember 2016)

vfl_freak hat gesagt.:


> Moin,
> 
> mit _*realloc*_ kannst Du allokierten Speicher erneut in anderer Größe allokieren.
> Ist allerdings ein gefährliches Spiel, sehr schnell fehlerhaft!
> ...



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 (12. Dezember 2016)

Joe1903 hat gesagt.:


> 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.



Joe1903 hat gesagt.:


> 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 (12. Dezember 2016)

cwriter hat gesagt.:


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



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


```
...
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);
}
...
```


----------



## cwriter (12. Dezember 2016)

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 (12. Dezember 2016)

cwriter hat gesagt.:


> 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.
> ...



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


----------



## cwriter (12. Dezember 2016)

Joe1903 hat gesagt.:


> Könntest du mir bitte einen Teil des Codes schreiben?


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


Joe1903 hat gesagt.:


> Ich benutze es auch nicht versprochen.


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


----------



## Joe1903 (12. Dezember 2016)

cwriter hat gesagt.:


> 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
> ...



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 


```
#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 (12. Dezember 2016)

Joe1903 hat gesagt.:


> Vielen Dank.Hier ist der ganze Code(nicht von mir).


Spricht für dich...


```
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


----------



## vfl_freak (13. Dezember 2016)

Moin,


cwriter hat gesagt.:


> Realloc funktioniert gut - die Frage ist nur, wie gut man damit umgehen kann.


ok, das meinte ich eigentlich, habe es vlt. unglücklich ausgedrückt 
Gruß Klaus


----------



## Joe1903 (13. Dezember 2016)

cwriter hat gesagt.:


> Spricht für dich...
> 
> 
> ```
> ...


Geht anstatt if(Input != stdin) auch if(Input != NULL)?


----------



## cwriter (13. Dezember 2016)

Joe1903 hat gesagt.:


> Geht anstatt if(Input != stdin) auch if(Input != NULL)?


Warum würdest du das wollen?
Nein, denn stdin ist != NULL. Daher würdest du in diesem if-Block mit ftell auf stdin zugreifen, was richtig doll verboten ist.

Gruss
cwriter


----------



## Joe1903 (14. Dezember 2016)

cwriter hat gesagt.:


> Warum würdest du das wollen?
> Nein, denn stdin ist != NULL. Daher würdest du in diesem if-Block mit ftell auf stdin zugreifen, was richtig doll verboten ist.
> 
> Gruss
> cwriter


Hallo.Ich habe dein Codesegment in den obigen Code eingebaut und kriege ich jetzt folgenden Fehler beim Kompilieren:

*ERROR CCN3275 ./6_zz.c:234   Unexpected text 'char' encountered.*



```
#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;
 size_t bufsize = 0;
 char *Word = NULL;

 if (Input != stdin)
 {
  size_t tmp = ftell(Input);
  fseek(Input, 0, SEEK_END);
  bufsize = ftell(Input);
  fseek(Input, tmp, SEEK_SET);

  Buf = (char*)malloc(bufsize);
  if (Buf == NULL)
  {
   return -1;
  }
  
  if (fread(Buf, sizeof(char), bufsize, Input) != bufsize)
  {
   printf("The promised amount was not read...\n");
  }
 }
 else
 {
  bufsize = 256;
  Buf = calloc(bufsize, sizeof(char));
  if (Buf == NULL)
  {
   return -1;
  }
  char* bufbase = Buf;
  printf("bufb: %p\nBuf : %p\n", bufbase, Buf);
 
  while (fgets(Buf, (int)(bufsize - (Buf - bufbase)), Input) != NULL)
  {
   Buf += (int)(bufsize - (Buf - bufbase)) -1;
   if (bufbase[bufsize - 2] != 0)
   {
    char* tmp = (char*)realloc(bufbase, bufsize * 2);
    if (tmp == NULL)
    {
     printf("Failed to realloc\n");
     free(bufbase);
     return -1;
    }
    Buf = tmp + (Buf - bufbase);
    bufbase = tmp;
    bufsize *= 2;
    bufbase[bufsize - 2] = 0;
   }
   else break;
  }
  Buf = bufbase;
 }

 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);
}


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);
}
```


----------



## vfl_freak (14. Dezember 2016)

Moin,

wie schon bei Deinem anderen  Beitrag geschrieben:
welche Zeile ist das ???

Ist es so schwer, das mal eben zu markieren?
Hier will doch keiner über 250 Zeilen durchzählen ..... 

Gruß Klaus


----------



## Joe1903 (14. Dezember 2016)

vfl_freak hat gesagt.:


> Moin,
> 
> wie schon bei Deinem anderen  Beitrag geschrieben:
> welche Zeile ist das ???
> ...


Sorry,das ist Zeile 242 in meinem obigen Post.


----------



## Joe1903 (14. Dezember 2016)

cwriter hat gesagt.:


> Warum würdest du das wollen?
> Nein, denn stdin ist != NULL. Daher würdest du in diesem if-Block mit ftell auf stdin zugreifen, was richtig doll verboten ist.
> 
> Gruss
> cwriter


Ok ich habe den Fehler gefunden und korrigiert.Jetzt funktioniert alles prima.Lediglich das Verhalten des Programms hat sich geändert:
Zuvor kam die Ausgabe bei Ctrl+D, nun kommst es bei Enter.Was muss ich ändern,damit es wieder so ist wie zuvor?Und die Reihenfolge der Wörter ist ja alphabetisch absteigend.
Ich möchte aber,dass bei Wörtern mit derselben Häufigkeit alphabetsich aufsteigend ist.An welcher Stelle kann ich das ändern?


----------



## cwriter (14. Dezember 2016)

Joe1903 hat gesagt.:


> Zuvor kam die Ausgabe bei Ctrl+D, nun kommst es bei Enter.


Ach, du willst mehrere Zeilen lesen?
Dann kannst du einfach das "else break;" entfernen. Bei dir Zeile 254.
Danach solltest du auf dieser Zeile ein Buf=bufbase + strlen(bufbase); hinmachen. Nicht optimal, aber es sollte gehen. Eine Optimierung wäre, wenn du direkt nach dem fgets() "size_t t = strlen(Buf)" bestimmen würdest und dann direkt nach dem if-Block ein Buf += t; machen würdest. Aber beides ist nicht optimal.
Dann hättest du wirklich die gesamte Eingabe im Speicher, und da wäre fread besser, da es dir sagt, wieviel gelesen wurde. Wenn du nur jede Zeile ganz haben willst, dann kannst du statt des "else break;" ein "else {Buf=bufbase; /*dein strtok-code von weiter unten*/}" schreiben.



Joe1903 hat gesagt.:


> Und die Reihenfolge der Wörter ist ja alphabetisch absteigend.


Na wenn du das sagst 



Joe1903 hat gesagt.:


> Ich möchte aber,dass bei Wörtern mit derselben Häufigkeit alphabetsich aufsteigend ist.


Scheint etwas mit dem Baum und nicht mehr mit dem fgets() zu tun haben.



Joe1903 hat gesagt.:


> An welcher Stelle kann ich das ändern?


Nun, ich schätze, dass da ein Fehler in Zeile 182 und oder 186 ist. Sollte ja wohl nicht dasselbe da stehen, oder?

Aber hier sind wir eigentlich schon bei was anderem als beim "dynamischen fgets()".
Du solltest in Betracht ziehen, dafür ein eigenes Thema (unabhängig von diesem hier) zu erstellen, damit sich Leute, die sich für fgets() interessieren, nicht mit Bäumen herumschlagen müssen und umgekehrt.

Gruss
cwriter


----------



## Joe1903 (22. Dezember 2016)

cwriter hat gesagt.:


> Warum würdest du das wollen?
> Nein, denn stdin ist != NULL. Daher würdest du in diesem if-Block mit ftell auf stdin zugreifen, was richtig doll verboten ist.
> 
> Gruss
> cwriter


Hi cwriter.
Ich habe den Code von dir in den Rest eingebaut und so geändert, dass es dem restlichen Code entspricht ("same behaviour").
Kannst du mal kurz anschauen,ob es OK wäre? Für mich ist es OK,aber ich hätte gerne deine Meinung gehört.


```
#include <stdlib.h>
#include <stdio.h>
#include <string.h>


#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+=-*/\\,.;:'#~?<>|{}[]`!\".$%^&()"



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


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);



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;
        size_t bufsize = 0;
        char *Word = NULL;
        char *bufbase = NULL;
        char *tmp = NULL;

        if (Input != stdin)
        {
                size_t tmp = ftell(Input);
                fseek(Input, 0, SEEK_END);
                bufsize = ftell(Input);
                fseek(Input, tmp, SEEK_SET);

                Buf = (char*)malloc(bufsize);
                if (Buf == NULL)
                {
                        Status = NO_MEMORY_FOR_WORDNODE;
                }

                if (fread(Buf, sizeof(char), bufsize, Input) != bufsize)
                {
                        printf("The promised amount was not read.\n"); //Brauch ich das hier wirklich?
                }
        }
        else
        {
                bufsize = 256;
                Buf = calloc(bufsize, sizeof(char));
                if (Buf == NULL)
                {
                        Status = NO_MEMORY_FOR_WORDNODE;
                }
                bufbase = Buf;
                while (fgets(Buf, (int)(bufsize - (Buf - bufbase)), Input) != NULL)
                {
                        Buf += (int)(bufsize - (Buf - bufbase)) -1;
                        if (bufbase[bufsize - 2] != 0)
                        {
                                tmp = (char*)realloc(bufbase, bufsize * 2);
                                if (tmp == NULL)
                                {
                                        Status = NO_MEMORY_FOR_WORDNODE;
                                }
                                Buf = tmp + (Buf - bufbase);
                                bufbase = tmp;
                                bufsize *= 2;
                                bufbase[bufsize - 2] = 0;
                        }
                        Buf = bufbase + strlen(bufbase);
                }
                Buf = bufbase;
        }

        Word = strtok(Buf, NONALPHA);
        while (Status == SUCCESS && Word != NULL)
        {
                Status = AddToTree(DestTree, Treecount, Word);
                if (Status == SUCCESS)
                {
                        Word = strtok(NULL, NONALPHA);
                }
        }
        free(Buf);
 free(bufbase);
        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(Result != NULL)
        {
                memcpy(Result, s, slen);
                *(Result + slen) = '\0';
        }

        return (Result);
}


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(Treecount == 0)
                {
                        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);
}
```


----------



## cwriter (22. Dezember 2016)

Joe1903 hat gesagt.:


> Kannst du mal kurz anschauen,ob es OK wäre?


"Wäre"? Bei welcher Bedingung?


> printf("The promised amount was not read.\n"); //Brauch ich das hier wirklich?


Wie meinst du? Das war/ist nur ein Platzhalter für einen Handler, der den Fall behandelt, dass nicht die erwartete Anzahl Bytes gelesen wurde. Im Code davor wurde mittels ftell() gefragt, wieviele Bytes denn in der Datei sind und die Antwort wurde in einer Variable gespeichert. Wenn jetzt maximal diese Grösse gelesen wird und fread() sagt, dass nicht die gesamte Menge gelesen wurde (ggf. weil die Datei in der Zwischenzeit verändert / gelöscht wurde oder das Laufwerk in tausend Splitter zerbarst), haben wir ein Problem.

Im Übrigen:
Hast du dir überlegt, was genau passiert, wenn ein Fehler wie "NO_MEMORY" auftaucht und der Pointer zum Speicher NULL ist, aber du das Programm nicht abbrichst? (Hast du nicht, dein Programm ist ein klassisches Schönwetterprogramm: Wenn alles gut geht (immer genug Speicher da ist), dann sollte es laufen. Wenn nicht, sagst du in deinem Code "¯\_(ツ)_/¯" und drehst der Katastrophe den Rücken)

Gruss
cwriter


----------



## Joe1903 (22. Dezember 2016)

cwriter hat gesagt.:


> "Wäre"? Bei welcher Bedingung?
> 
> Wie meinst du? Das war/ist nur ein Platzhalter für einen Handler, der den Fall behandelt, dass nicht die erwartete Anzahl Bytes gelesen wurde. Im Code davor wurde mittels ftell() gefragt, wieviele Bytes denn in der Datei sind und die Antwort wurde in einer Variable gespeichert. Wenn jetzt maximal diese Grösse gelesen wird und fread() sagt, dass nicht die gesamte Menge gelesen wurde (ggf. weil die Datei in der Zwischenzeit verändert / gelöscht wurde oder das Laufwerk in tausend Splitter zerbarst), haben wir ein Problem.
> 
> ...


Ok danke für den Hinweis.Ich schreib morgen den Code um und poste es hier.Wäre dankbar,wenn du es dir anschauen könntest.


----------



## Joe1903 (2. Januar 2017)

cwriter hat gesagt.:


> "Wäre"? Bei welcher Bedingung?
> 
> Wie meinst du? Das war/ist nur ein Platzhalter für einen Handler, der den Fall behandelt, dass nicht die erwartete Anzahl Bytes gelesen wurde. Im Code davor wurde mittels ftell() gefragt, wieviele Bytes denn in der Datei sind und die Antwort wurde in einer Variable gespeichert. Wenn jetzt maximal diese Grösse gelesen wird und fread() sagt, dass nicht die gesamte Menge gelesen wurde (ggf. weil die Datei in der Zwischenzeit verändert / gelöscht wurde oder das Laufwerk in tausend Splitter zerbarst), haben wir ein Problem.
> 
> ...


Kannst du mal nur einem Beispiel zeigen,wie du das machen würdest?
Im Übrigen finde ich nicht,dass es ein Schönwetterprogramm ist,denn sobald eine Funktion als Status etwas anderes als SUCCESS zurückgibt,macht es zwar weiter,aber macht im Grunde "nichts" mehr im Main und gibt den Fehler aus.


----------



## Joe1903 (2. Januar 2017)

Und kannst du mal ob schauen ob das korrekt ist?


```
int ReadInputToTree(
        WORD **DestTree,
        size_t *Treecount,
        FILE *Input)
{
        int Status = SUCCESS;
        char *Buf = NULL;
        size_t bufsize = 0;
        char *Word = NULL;
        char *bufbase = NULL;
        char *tmp = NULL;


        if (Input != stdin)
        {
                size_t tmp = ftell(Input);
                fseek(Input, 0, SEEK_END);
                bufsize = ftell(Input);
                fseek(Input, tmp, SEEK_SET);


                Buf = (char*)malloc(bufsize);
                if (Buf == NULL)
                {
                        Status = NO_MEMORY_FOR_WORDNODE;
                           goto end;
                }


                if (fread(Buf, sizeof(char), bufsize, Input) != bufsize)
                {
                        printf("The promised amount was not read.\n");
                           exit(1);
                }
        }
        else
        {
                bufsize = 256;
                Buf = calloc(bufsize, sizeof(char));
                if (Buf == NULL)
                {
                        Status = NO_MEMORY_FOR_WORDNODE;
                        goto end;
                }
                bufbase = Buf;
                while (fgets(Buf, (int)(bufsize - (Buf - bufbase)), Input) != NULL)
                {
                        Buf += (int)(bufsize - (Buf - bufbase)) -1;
                        if (bufbase[bufsize - 2] != 0)
                        {
                                tmp = (char*)realloc(bufbase, bufsize * 2);
                                if (tmp == NULL)
                                {
                                        Status = NO_MEMORY_FOR_WORDNODE;
                                        goto end;
                                }
                                Buf = tmp + (Buf - bufbase);
                                bufbase = tmp;
                                bufsize *= 2;
                                bufbase[bufsize - 2] = 0;
                        }
                        Buf = bufbase + strlen(bufbase);
                }
                Buf = bufbase;
        }


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

end:
        free(Buf);
        free(bufbase);
        return (Status);
}
```


----------



## cwriter (2. Januar 2017)

Joe1903 hat gesagt.:


> Im Übrigen finde ich nicht,dass es ein Schönwetterprogramm ist,denn sobald eine Funktion als Status etwas anderes als SUCCESS zurückgibt,macht es zwar weiter,aber macht im Grunde "nichts" mehr im Main und gibt den Fehler aus.


Hm.
Es wurde auf den NULL-Pointer zugegriffen. D.h. das Programm ist abgestürzt. Mit den neuen gotos geht es, ist aber nicht schön (und schlechter für den Computer auszuführen, Stichwort pipeline).
Mal abgesehen davon ist auch Status als Variable überflüssig, denn es ist kein Status (den die Funktion selbst kennen muss), sondern ein Rückgabewert. Aber ist ist dann schon näher an der Maschine.

Das Problem: Dein "Nichts" ist UB. Und das ist schlecht.


Joe1903 hat gesagt.:


> Kannst du mal nur einem Beispiel zeigen,wie du das machen würdest?


Nein, da ich nicht weiss, was du meinst.

Gruss
cwriter

/EDIT: Zu deinem neuen Code: Beim Überfliegen habe ich nichts gefunden. Aber das muss nichts heissen.


----------

