Dynamisches fgets in C

Joe1903

Mitglied
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
Geht anstatt if(Input != stdin) auch if(Input != NULL)?
 

Joe1903

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


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

Premium-User
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

Mitglied
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

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

Und die Reihenfolge der Wörter ist ja alphabetisch absteigend.
Na wenn du das sagst :)

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.

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
 
Zuletzt bearbeitet:

Joe1903

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

C++:
#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

Erfahrenes Mitglied
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