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 machenchar **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 denmalloc
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); bekommtprintf
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 Standardfunktionstrtok
)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 demmalloc
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 einchar*
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 mitstrncpy
(oderstrncat
) 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, iststrcat
wiestrcpy
.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.