Wörter alphabetisch ordnen



  • Hallo,

    ich möchte einen Text von der Tastatur einlesen und dann die Häufigkeit der einzelnen Wörter bestimmen.
    Der Teil ist mir klar und den habe ich bereits geschafft.

    Gibts nen Trick, wie man die eingelesenen Wörter am Ende alphabetisch anordnen kann und dahinter bzw davor deren Häufigkeiten ausgeben lassen kann?

    #include <stdio.h>
    #include <ctype.h>
    #include <stdlib.h>
    #include <string.h>
    
    size_t woerter(char *text)
    {
        size_t word_count = 0;
        int is_space, was_space;
        for (was_space = 1; *text; was_space = is_space)
            if (!(is_space = isspace(*text++)) && was_space)
                word_count++;
        return word_count;
    }
    
    int main()
    {
    	int i = 0;
        char text[100];
        puts("Geben sie beliebige Woerter ein:");
        fgets(text, sizeof(text) / sizeof(text[0]), stdin);
        printf("Es sind %d Woerter\n", woerter(text));
        return 0;
    	getchar();
    }
    

    Mein Gedankengang: Ich muss jedes Wort in einem neuen Char Array speichern.

    void String_Vergleich(char s1[], char s2[]) {
       int ret = strcmp (s1, s2);
       if(ret == 0)
          printf("%s == %s\n", s1, s2);
       else
          printf("%s %c %s\n",s1,( (ret < 0) ?'<' :'>'), s2);
    }
    

    Mit einer for-Schleife lasse ich dann diese Funktionen für die Anzahl aller Arrays durchlaufen (wsh am einfachsten mit einem counter zu realisieren, der mit zählt, wenn ein neues Array erstellt wird).

    Meine Frage: Wie kann ich jedes Wort in einem neuen Char Array speichern?

    MfG



  • Indem du deinen eingelesenen String in einzelne Strings/Wörter aufteilst. Das geht z.B. mit einer Erweiterung deiner Funktion "woerter", die dann nicht nur zählt, sondern auch gleich Wörter in deine Stringliste einträgt.
    Dazu kannst du z.B. char*** als Zeiger auf eine Stringliste übergeben.



  • Die Erweiterung ist mir klar, deren Umsetzung noch nicht.

    Hab mich etwas zu Pointer informiert und den Möglichkeiten. Entweder Pointer oder 2D Array.

    Wenn ich jetzt ersteres wähle:

    char *strs[NUMBER_OF_STRINGS];
    
    char *strs[NUMBER_OF_STRINGS] = {"foo", "bar", "bletch", ...};
    
    strs[i] = "bar";
    strs[i] = "foo";
    

    Sieht genau nach dem aus, was ich bräuchte, allerdings muss laut meinem Compiler (Visual Studio 2010) NUMBER_OF_STRINGS eine Konstante sein... wie kann man die Anzahl der Strings veränderbar machen bzw. mit nem Zähler pro Wort erhöhen?

    Das Wort hätte ich jetzt so eingelesen:

    if (!(is_space = isspace(*text++)) && was_space)
                word_count++;
                NUMBER_OF_STRINGS++;
    			char *strs[NUMBER_OF_STRINGS] = text;
    

    Problem: Stringanzahl will noch nicht so und auf die Weise kann ich das "String-Array" nicht initialisieren, weil {....} erwartet wird.



  • Dein Stichwort lautet: malloc 😉



  • Das Stichwort ist mal wieder gold wert 😉

    EDITIERTER CODE:

    #include <stdio.h>
    #include <ctype.h>
    #include <stdlib.h>
    #include <string.h>
    #include <conio.h>
    
    size_t woerter(char *text)
    {
    
        size_t word_count = 0;
    
        int is_space, was_space;
    
    	char * Stringarray; // neu
    	Stringarray = (char *) malloc(100); // neu
    
        for (was_space = 1; *text; was_space = is_space)
            if (!(is_space = isspace(*text++)) && was_space)
                word_count++;	
    
    			Stringarray[word_count] = *text; // neu
    			printf(Stringarray);
    
        return word_count;
    
    }
    
    int main(void)
    {
    	int i = 0;
        char text[100];
    
    	puts("Geben sie beliebige Woerter ein:");
        fgets(text, sizeof(text) / sizeof(text[0]), stdin);
        printf("Es sind %d Woerter\n", woerter(text));
    
    	getch();
       return 0;
    
    }
    

    Läuft durch. Im Moment gibt er mir allerdings bei printf(Stringarray) nur ein einziges = aus, egal wieviel Wörter ich einschreibe..

    Sollte nur ein Test sein um zu sehen, ob er fleißig einspeichert, aber da hat sich glaub ich ein Fehler eingeschlichen.



  • Wie siehts mit free() aus!?



  • Also ich bekomme mit dem obigen Code so viele = wiedergegeben wie Wörter.

    Wenn ich free(Stringarray) vor printf reinmache in der Funktion gibt er mir 100 komische Zeichen aus, bei free() nach printf kommt wieder nurn =...

    Irgendwo gibts wohl noch Probleme...

    Bei

    printf("%s", Stringarray[word_count]);
    

    gibt er mir <null> aus... also wird der Wert noch nicht zugewiesen. Weiß jemand warum?



  • Ich bin mir nicht sicher, wie deine Funktion arbeiten soll, aber ich denke, dass du ein paar Klammern vergessen hast:

    for (was_space = 1; *text; was_space = is_space)
            if (!(is_space = isspace(*text++)) && was_space){
                word_count++;	
                Stringarray[word_count] = *text; // neu
                printf(Stringarray);
            }
    

    Mit

    Stringarray[word_count] = *text; // neu
    

    kopierst du nur ein Zeichen nach Stringarray (ein deinem Fall sogar immer '\0').
    Und da du word_count vorher inkrementierst, sieht Stringarray am Ende immer so aus:

    Stringarray[0] == Muell //Bei dir zufaelliger Weise scheinbar immer '='
    Stringarray[1] == '\0'
    Stringarray[2 ... 99] == wieder Muell
    

    char* ist der falsche Datentyp, wenn du eine beliebige Anzahl Zeichenketten unterschiedlicher Laenge speichern moechtest. Du brauchst char**.

    Zum Kopieren reicht eine Zuweisung nicht aus. Guck dir mal strcpy() an...

    Gruß
    M



  • Ach Mist, jetzt habe ich mich vertan.

    M^M schrieb:

    Stringarray[0] == Muell //Bei dir zufaelliger Weise scheinbar immer '='
    Stringarray[1] == '\0'  //STIMMT NICHT
    Stringarray[2 ... 99] == wieder Muell
    

    M

    Das '\0' steht in Stringarray[ANZAHL_WOERTER] und nicht unbedingt in Stringarray[1]

    Das free rufst du auf, wenn du Stringarray nicht mehr benoetigst, also spaetestens vor return word_count;.

    Gruß
    M



  • Da läuft bei dir noch etwas durcheinander.

    Bei

    char * Stringarray; // neu
        Stringarray = (char *) malloc(100); //
    

    hast du kein Stringarray, sondern nur einen Speicher für einen String.

    Das was du mit char *strs[NUMBER_OF_STRINGS]; haben wolltest, musst du mit einem Doppelzeiger machen

    char **Stringarray; // neu
        Stringarray = malloc(100 * sizeof(*Stringarray));  // Jetzt hast du Platz für 100 Zeiger auf char*
    

    Da bleibt noch die Frage, wo die Zeiger hinzeigen sollen.
    Im Augenblick sieht es so aus, dass die Zeiger noch nach text verweisen.
    Die Wöerter sind dadurch nicht Nullterminiert.



  • size_t woerter(char *text)
    {
    
        size_t word_count = 0;
    
        int is_space, was_space;
    
    	char **Stringarray; // neu
        Stringarray = malloc(100 * sizeof(*Stringarray));  // neu
    
        for (was_space = 1; *text; was_space = is_space)
            if (!(is_space = isspace(*text++)) && was_space)
       			Stringarray[word_count] = *text; // neu
    			printf("%s", Stringarray); // neu
    			free(Stringarray); // neu
    			// gibt mir nun z.B. beim Wort "test" nur den Buchstaben e aus. 
    		    word_count++;
                return word_count;
    
    }
    

    Visual Studio meckert bei der Zuweisung: "...."void *" kann keiner Entität vom Typ char ** zugewiesen werden bzw "char" kann keiner Entität vom Typ "char *" zugewiesen werden"

    Allerdings läuft das Programm problemlos durch.

    Durch die Änderung gibt er mir nur den Buchstaben e aus beim Wort test, bei test test auch nur e, also immer die zweite Stelle.

    Das mit dem Pointer verstehe ich, also im Moment erstelle ich eigentlich nur einen String, den es in c ja so nicht gibt, also ein char Array mit der Länge meiner Eingabe.

    Mit (Spekulation)

    strcpy(Stringarray, text)
    

    müsste ja dann immer ein Array gefüllt werden, oder denke ich da falsch?



  • Bei Fehlermeldungen ist die Angabe der dazu gehörigen Zeilen-/Spaltennummer zwingend, damit man auch weiß, wo der Fehler auftritt.

    `Stringarray ist vom Typ char**

    Stringarray[word_count] ist vom Typ char*

    text ist vom Typ char*

    *text ist vom Typ char`

    Sinnvoll ist hier eigentlich nur die Zuweisung gleicher Typen.

    size_t woerter(char *text)
    {  
        size_t word_count = 0;
    
        int is_space, was_space;
    
        char **Stringarray; 
        Stringarray = malloc(100 * sizeof(*Stringarray));  
    
        for (was_space = 1; *text; was_space = is_space)
            if (!(is_space = isspace(*text++)) && was_space)
                Stringarray[word_count] = *text; // char* = char -> passt nicht
    // hier ist die for-Schleife vorbei
        printf("%s", Stringarray);// printf erwartet char*, bekommt aber char** -> passt nicht
        free(Stringarray); 
    
        word_count++;  // sollte das nicht in der Schleife / im if sein?
        return word_count;
    }
    

    C-Tabaluga schrieb:

    Mit (Spekulation)

    strcpy(Stringarray, text)
    

    müsste ja dann immer ein Array gefüllt werden, oder denke ich da falsch?

    Typproblem siehe oben.
    Und du brauchst auch Speicher in den du kopieren darfst. Den hast du mit dem einen malloc noch nicht bekommen.

    Mit dem malloc in Zeile 8 hast du Platz für 100 Zeiger auf char*. Den Speicher wo die dann hin zeigen hast du damit noch nicht.



  • Sorry, das mit der Zeilesache, soweit hätte ich denken müssen.

    size_t woerter(char *text)
    {  
        size_t word_count = 0;
    
        int is_space, was_space;
    
        char **Stringarray;
        Stringarray = malloc(100 * sizeof(*Stringarray));  // hier befindet sich ein Fehler (falsche Typzuweisung)
    
        for (was_space = 1; *text; was_space = is_space) 
    	{
            if (!(is_space = isspace(*text++)) && was_space) 
    	     {
    
    			 word_count++;
    			 Stringarray[word_count] = text;  // damit will ich ein Wort einzeln deklarieren und in ein char Array Stringarray[i] legen bzw später kopieren
       //          free(Stringarray); //  Durch das malloc soll man das Array bei nicht benutztem Speicherplatz wieder räumen.
    
    		}
    	}
    
    		  printf("%s", Stringarray);  // das soll testweise ausgeben, ob der Compiler meinen Wunsch erfüllt
    		  return word_count;
    
    }
    

    Deklarationsprobleme bis auf das im Kommentar erwähnte nun beseitigt.

    Erwartet wird von mir als Ausgabe bei der Eingabe test also test. Bei Eingabe von test bekomme ich aber nur kryptische Zeichen und =.

    Irgendwo häng ich fest, als dass ich wegkomme von dem Fehler. Das Behandlen von Strings fällt mir schwerer als ich dachte in C, aber es immer schön solche Leute wie euch zu haben, die einem bei der größten Verzweiflung helfen. Danke dafür!



  • Deine Ausgabe ist falsch

    DirkB schrieb:

    printf("%s", Stringarray);// printf erwartet char*, bekommt aber char** -> passt nicht
    

    Dein Stringarray ist ein Feld, das 100 Zeiger auf char -Zeiger speichern kann.

    text ist ein Array von 1024 char .

    Nehmen wir mal an, text fängt an der Speicheradresse 10000 an.
    Der Speicher den malloc für Stringarray reserviert hat liegt bei 44444. In Stringarray ist also 44444 gespeichert.

    Nach deinem Programm sollte das dann so aussehen:

    char text[1024] =      "Hallo Welt, dies ist ein Text"
                            ^     ^     ^    ^   ^   ^        
    Stringarray[0] = 10000 -+     |     |    |   |   |          // bei Adresse 44444
    Stringarray[1] = 10006 -------+     |    |   |   |          // bei Adresse 44448
    Stringarray[2] = 10012 -------------+    |   |   |          // bei Adresse 44452
    Stringarray[3] = 10017 ------------------+   |   |          // bei Adresse 44456
    Stringarray[4] = 10021 ----------------------+   |          // bei Adresse 44460
    Stringarray[5] = 10025 --------------------------+          // bei Adresse 44464
    

    Wenn du jetzt das "ein" haben willst, dann musst du an printf dei Anfangsadresse (10021) übergeben.
    Diese ist in Stringarray[4] gespeichert.
    Durch das printf("%s", Stringarray); bekommt printf aber die Adresse 44444. Da stehen aber nur Adressen und kein sinnvoller Text.

    Wie du auch siehst, sind die Wörter weiterhin in text abgelegt und

    DirkB schrieb:

    Die Wöerter sind dadurch nicht Nullterminiert.

    Stringarray[4] liefert als nicht nur "ein" sondern "ein Text".



  • Okay, das mit der Ausgabe ist mir nun klar.

    printf("%s", Stringarray[1]);
    

    Beim Wort test gibt er mir dann est aus. Also noch ein kleiner Zuweisungfehler mit dem ersten Wort. Wie du auch richtig sagst, gibt er mir bei test test z.B. est test aus, also er trennt nicht, was mit der von dir erwähnten Nullterminierung zusammenhängt. Diesbezüglich habe ich mich versucht zu informieren, aber ich finde da keinen Ansatz, der mir hierbei weiterhelfen würde.

    Wenn ich z.B. sagen würde: text = 0 oder text = ""; dann gibt er mir trotzdem immer alle Wörter aus.

    Ich versteh, was du meinst und was du erreichen willst, aber ich weiß nicht, wie ichs umsetzen soll.

    Ich muss ja erst sagen

    Stringarray[word_count] = text;
    

    aber das ist ja eigentlich schon zu spät, weil da gibt er ja auf Platz 1 den ganzen Text ein. Also muss irgendwo vorher getrennt werden bzw. der Wert von text wieder auf null gesetzt werden.



  • Kann mir jemand nen Tipp geben?



  • Das mit dem "est" mag daran liegen, dass du bei *isspace(test++) schon den Postinkrement hast.

    Für die einzelnen Wörter gibt es mehrer Möglichkeiten:
    Du schreibst an die Stellen, wo ein Whitespace ist, eine '\0' in text.
    Dann ist text aber zerstört. (so macht es die Standardfunktion strtok )

    Oder du beschaffst dir für die Wörter auch Speicher mit malloc und kopierst die Wörter dann da rein.

    Nimm mal den Debugger und geh im Einzelschritt durch die Funktion. Lass dir dabei die Variablen anzeigen.
    Dann siehst du, was wann wo passiert.



  • Grundidee habe ich verstanden und würde ich so umsetzen:

    #include <stdio.h>
    #include <ctype.h>
    #include <stdlib.h>
    #include <string.h>
    #include <conio.h>
    
      size_t woerter(char *text)
    {  
        size_t word_count = 0;
        int a;
    	a = 100;
        int is_space;
    	int was_space;
    
       char **Stringarray; // neu
        Stringarray = (char**) malloc(100 * sizeof(*Stringarray));  
    
        char *woerter;
    	woerter = (char*) malloc (a * sizeof(woerter)); // hier will ich die wörter reinlegen
    
        for (was_space = 1; *text; was_space = is_space) 
    	{
            if (!(is_space = isspace(*text++)) && was_space) 
    	     {
                             *text--;
    			 strcpy(woerter, text); //würde der so richtig kopieren?
    			 word_count++;
    
    		}
    	}
    
    		  return word_count;
    
    }   
    
    int main(void)
    {
    	int i = 0;
        char text[100];
    
    	puts("Geben sie beliebige Woerter ein:");
        fgets(text, sizeof(text) / sizeof(text[0]), stdin);  // Verwendung der Funktion
        printf("Es sind %d Woerter\n", woerter(text));
    
    	getch();
       return 0;
    
    }
    

    Ich gebe mehrere Wörter ein und er gibt mir nur das erste aus. Wie kann ich das nun wiederholen, dass er im Array eins weitergeht?Also ich weiß im Moment noch nicht, wie ich das jetzt in das Stringarray reinmachen soll...



  • C-Tabaluga schrieb:

    char *woerter;
    woerter = (char*) malloc (a * sizeof(woerter)); // hier will ich die wörter reinlegen
    

    Bei dem sizeof in dem malloc wird nicht ein * weg gelassen, sondern es wird einmal dereferenziert.
    Du willst ja die Größe von dem Element bestimmen, auf den woerter zeigt.
    Also kommt da ein * hin. : *sizeof(woerter)
    Du kannst auch sizeof(woerter[0]) (und auch Stringarray = malloc(100 * sizeof(Stringarray[0])); )machen, wenn dir das einsichtiger ist.

    Du hast jetzt einen Speicherbereich für alle Wörter. Da wäre es am einfachsten du machst ein strcpy(woerter, text); und veränderst die Trennzeichen dann in woerter.

    Eigentlich war das aber so gedacht, dass du für jedes gefundene Wort Speicher anforderst und dieses dann von text in den Speicher kopierst. In der Schleife.

    C-Tabaluga schrieb:

    *text--;
                 strcpy(woerter, text); //würde der so richtig kopieren?
    

    Ein

    strcpy(woerter, text-1);
    

    macht es auch, ohne dass du an text rum spielst.

    Du kommst hier mit nur einem char* nicht mehr weiter. Du musst dir den Anfang vom Wort merken und dann noch ein char* haben, der die Zeichen untersucht (dein bisheriges text).

    Die Wortlänge ergibt sich dann aus der Differenz der beiden Zeiger.

    Wenn du für den Wortspeicher calloc nimmst, kannst du die Wörter auch mit strncpy (oder strncat ) da hin kopieren.
    calloc löscht den Speicher gleich. strncpy kopiert keine '\0', wenn die Anzahl der Zu kopierenden Zeichen erreicht ist. Wenn das Ziel leer ist, ist strcat wie strcpy . strncat hängt aber immer eine '\0' an.
    (Ich hoffe du hast die n bei den strncpy und strncat bemerkt)



  • C-Tabaluga schrieb:

    Ich gebe mehrere Wörter ein und er gibt mir nur das erste aus. Wie kann ich das nun wiederholen, dass er im Array eins weitergeht?Also ich weiß im Moment noch nicht, wie ich das jetzt in das Stringarray reinmachen soll...

    Na mit dem word_count. Das stand doch schon da.
    Stringarray ist ein Array mit Zeigern auf char*

    char text[1024] =       "Hallo Welt, dies ist ein Text"
                             vvvvv ||||| |||| ||| ||| ||||           
    Stringarray[0] = 20000  "Hallo"vvvvv |||| ||| ||| ||||          
    Stringarray[1] = 20032        "Welt,"vvvv ||| ||| ||||          
    Stringarray[2] = 20064              "dies"vvv ||| ||||           
    Stringarray[3] = 20096                   "ist"vvv ||||             
    Stringarray[4] = 20128                        "ein"vvvv
    Stringarray[5] = 20150                            "Text"     
    Stringarray[6] = NULL   // als Endekennung      
    Die Adressen ab 20000 (Beispiel) werden alle einzeln mit malloc/calloc beschafft. Die vvvv stellen das kopieren mit strncpy dar
    

    Du musst hier die Indizes von Stringarray durch word_count ersetzen.


Log in to reply