struct-anzahl mit fread/fwrite übergeben
-
Neben den Fehlern, die CStoll gefunden hat, noch folgendes:
Da du ja im wesentlichen meine Verbesserungen verwendet hast,
konnte ich schnell herausfinden, was bei dir schief läuft.
Ich habe die Datensätze, die mittels titel_eingeben hinzugefügt
werden, immer direkt in die Datei geschrieben, so dass dein
Array eingabe[100] nie gefüllt war.Wenn du nun nach einem Titel suchen möchtest, mußt du vorher
die Daten aus der Datei einlesen, oder direkt in der Datei
suchen, was natürlich länger dauert, wenn du öfters suchst.In Zeile 11:
scanf("%15[a-z,A-Z]",&suchen);
ist das &-Zeichen vor suchen verkehrt.
Außerdem solltest du bei der titel_eingabe den gleichen
Befehl zum Eingeben verwenden, wie nachher beim Suchen.
Z.B. habe ich in meiner musik_db.txt (übrigens ist es keine Textdatei,
sondern eine binäre, finde daher den Suffix .txt ungünstig) den
Titel "Hallo 2". Diesen kann ich mit deiner Routine nicht finden!Hier mal der verbesserte Code:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define clrscr() system("cls"); struct musiktitel { char titelname[16]; char interpret[16]; int dauer; }; static void remove_nextline(char *s) { unsigned int i; for (i=0; i<strlen(s); ++i) { if (s[i] == '\n') s[i] = 0; } } static int eingabe_ja(void) { char answer = 0;/*vor der schleife answer=0*/ while (!(answer == 'J' || answer == 'N')) { printf("Eingabe (J/N): "); scanf("%c", &answer); getchar(); answer = toupper(answer);/*wandelt klein in großbuchstaben um*/ } if (answer == 'J') return 1;/*wenn answer=J, rückgabe=1*/ return 0;/*ansonsten 0*/ } static void datei_schreiben(struct musiktitel *eingabe) { FILE *datei=fopen("musik_db.txt","a+b"); fseek(datei, 0 , SEEK_END);/*ende der datei*/ fwrite(eingabe, sizeof(struct musiktitel), 1, datei);/*daten werden von der adresse eingabe in den stream datei geschrieben*/ fclose(datei); } static int datei_lesen(struct musiktitel *eingabe, int size) { struct musiktitel tmp; int i = 0; FILE *datei=fopen("musik_db.txt", "rb"); memset(eingabe, 0x0, size*sizeof(struct musiktitel)); while (fread(&tmp, sizeof(struct musiktitel), 1, datei) > 0 && i<size) { eingabe[i++] = tmp; } return i; } static void titel_suchen(void) { struct musiktitel eingabe[100]; char suchen[16]; /* Deklaration Typ String max 15 Zeichen */ int a, f, b, size; /* Deklaration */ clrscr(); printf("Sie haben sich fuer 'Titel suchen' entschieden!\n\n"); printf("Lese Daten aus Datei...\n"); size = datei_lesen(eingabe, 100); printf("%d Daten eingelesen!\n", size); printf("Bitte geben Sie den Titelnamen des gesuchten Stuecks ein:\n\t\t"); scanf("%15[a-z,A-Z]",suchen); /* <-- hier war ein fehler */ getchar(); printf("\nsie gaben ein:%s\n",suchen); for (a=0; ((a<size)&& (b!=1)); a++) { f = strcmp(suchen, eingabe[a].titelname); if (f == 0) { printf("\n\t\tTitel gefunden\n\n\n"); printf("Titelname\t\tInterpret\t\tDauer\n\n"); printf("%-15s\t%-15s\t%i\n", eingabe[a].titelname, eingabe[a].interpret, eingabe[a].dauer); b=1; } else b=2; } if (b == 2){ printf("\n\nDer gesuchte Teilnehmer konnte nicht gefunden werden.\n\n"); printf("a:%i, b:%i",a,b); } } static void titel_eingeben(void) { struct musiktitel eingabe; char answer; clrscr(); printf("Sie haben sich fuer 'Titel eingeben' entschieden!\n\n"); do { printf("Geben sie jetzt zuerst den Titel an!(Maximal 15 Zeichen)\n\n"); fgets(eingabe.titelname, 15, stdin); remove_nextline(eingabe.titelname); clrscr(); printf("Titelname:%15s\n\n", eingabe.titelname); printf("Jetzt bitte den Interpreten!(Maximal 15 Zeichen)\n\n"); fgets(eingabe.interpret, 15, stdin); remove_nextline(eingabe.interpret); clrscr(); printf("Titelname:%15s\n",eingabe.titelname); printf("Interpret:%15s\n\n",eingabe.interpret); printf("Wie lange dauert der Titel? (Angaben in Sekunden)\n\n"); scanf("%4i",&eingabe.dauer); getchar(); clrscr(); printf("Ihre Angaben waren wie folgt:\n\n"); printf("Titelname:%15s \n",eingabe.titelname); printf("Interpret:%15s \n",eingabe.interpret); printf("Titellaenge:%13is\n\n",eingabe.dauer); printf("Ist das korrekt?\n"); if (eingabe_ja()) { datei_schreiben(&eingabe); printf("Ihre Angaben wurden gespeichert!\n\n"); } } while (answer == 'N'); } static void titel_ausgeben(void) { struct musiktitel eingabe; FILE *datei=fopen("musik_db.txt","rb"); clrscr(); rewind(datei);/*stream-zeiger an anfang*/ printf("\t***********************************************\n"); printf("\t*< Musik-Datenbank >*\n"); printf("\t***********************************************\n"); printf("\t< Titelname <> Interpret <> Dauer >\n"); printf("\t***********************************************\n"); while (fread(&eingabe, sizeof(struct musiktitel), 1, datei) > 0) {/*liest aus dem stram datei in eingabe,größe musiktitel*/ printf("\t-----------------------------------------------\n"); printf("\t< %-15s %-15s %4is >\n", eingabe.titelname, eingabe.interpret, eingabe.dauer); printf("\t-----------------------------------------------\n"); } fclose(datei); printf("\t***********************************************\n\n"); } static int menue(void) { int result = -1; clrscr(); while (1) { printf("Musik-Datenbank\n\n"); printf("Musiktitel eingeben 1\n"); printf("Musiktitel ausgeben 2\n"); printf("Musiktitel suchen 3\n\n"); printf("Bitte waehlen Sie: "); scanf("%d", &result); getchar(); if (!(result == 1 || result == 2 || result == 3)) { printf("Sie haben eine falsche Zahl eingegeben!\n"); printf("Bitte korrigieren Sie ihre Eingabe: "); } else { break; } } return result; } int main(void) { int eingabe = 0; int zaehler = 0;/*zähler startet bei 0*/ do { int result; result = menue(); switch (result) { case 1: zaehler++;/*zähler+1*/ titel_eingeben(); break; case 2: titel_ausgeben(); break; case 3: titel_suchen(); break; default:break; } printf("\n"); printf("Sie haben %d Titel eingegeben!\n\n", zaehler); printf("Moechten sie eine neue Auswahl treffen?\n"); eingabe = eingabe_ja(); } while (eingabe); return 0; }
Hier sind noch ein paar Fehler drinnen, korrigiert die doch mal.
Gruß mcr
-
ok jetzt funktionier alles. vielen dank euch beiden, vorallem dir mcr, ohne dich hätt ich das sicher nicht hinbekommen!!!
tausend dank und grüße
julian
-
zwei fragen sind jetz noch aufgetaucht.
un zwar könntest du mir als leihen mal erklären, was genau hier passiert??[cpp]static void remove_nextline(char *s) { unsigned int i; for (i=0; i<strlen(s); ++i) {/*länge des strings*/ if (s[i] == '\n') s[i] = 0; } }[/cpp]
prinzipiell ist mir das schon klar, aber nicht im detail...
und hiermit komm ich auch nochnicht wirklich klar:
[cpp]static int datei_lesen(struct musiktitel *eingabe, int size) { struct musiktitel tmp; int i = 0; FILE *datei=fopen("musik_db.txt", "rb"); memset(eingabe, 0x0, size*sizeof(struct musiktitel));/*schreibt eingabe in die ersten bites von struct*/ while (fread(&tmp, sizeof(struct musiktitel), 1, datei) > 0 && i<size) { eingabe[i++] = tmp; } return i; }[/cpp]
was genau macht denn jetzt memset? habe dafür keine richtige erklärung gefunden...
grüße un danke^^
julian
-
Hallo Julian,
julian06 schrieb:
zwei fragen sind jetz noch aufgetaucht.
un zwar könntest du mir als leihen mal erklären, was genau hier passiert??static void remove_nextline(char *s) { unsigned int i; for (i=0; i<strlen(s); ++i) {/*länge des strings*/ if (s[i] == '\n') s[i] = 0; } }
prinzipiell ist mir das schon klar, aber nicht im detail...
strlen liefert die Länge des Strings zurück, genauer gesagt die Anzahl
der Chars bis zum nächsten '\0'. Die for-schleife ersetzt alle Vorkommen
des Zeichens '\n' durch '\0', dem String-Ende-Zeichen.
Das Problem besteht darin, dass fgets das '\n' mit in die Variable speichert
und du das mit Sicherheit nicht in deinen Datensätzen haben möchtest.
Kannst die Funktion ja mal entfernen, um zu sehen, was passiert.*char *fgets(char *s, int size, FILE stream);
fgets() liest hochstens size minus ein Zeichen von stream und
speichert sie in dem Puffer, auf den s zeigt. Das Lesen stoppt nach
einem EOF oder Zeilenvorschub. Wenn ein Zeilenvorschub gelesen wird,
wird er in dem Puffer gespeichert. Ein '\0' wird nach dem letzten
Zeichen im Puffer gespeichert.julian06 schrieb:
und hiermit komm ich auch nochnicht wirklich klar:
static int datei_lesen(struct musiktitel *eingabe, int size) { struct musiktitel tmp; int i = 0; FILE *datei=fopen("musik_db.txt", "rb"); memset(eingabe, 0x0, size*sizeof(struct musiktitel)); while (fread(&tmp, sizeof(struct musiktitel), 1, datei) > 0 && i<size) { eingabe[i++] = tmp; } return i; }
was genau macht denn jetzt memset? habe dafür keine richtige erklärung gefunden...
grüße un danke^^
julianmemset initialisiert size*sizeof(struct musiktitel) Bytes mit 0.
void *memset(void *s, int c, size_t n);
Die Funktion memset() schreibt n mal die Bytekonstante c in den
Speicherbereich, auf den s zeigt.Die while-schleife liest, solange Datensätze in der Datei vorhanden sind,
diese ein und speichert sie im Array eingabe[].Noch Fragen?
-
Aber btw, du solltest dir mal ein gutes C-Buch kaufen und ein wenig
darin nachlesen.Noch einen Tip: google mal nach "man memset", dann hättest du auch
die Erklärung zu memset gefunden.Ach ja, darf ich fragen, welches Betriebssystem verwendest?
-
morgen. vielen dank erstmal. ich hab mir bereits den schinken c von a bis z zugelegt, konnte aber zum thema memset nichts befriedigendes finden. ich benutze übrigens winxp.
also wenn ich das jetzt richtig verstanden habe, wandelt die funktion remove_nextline alle /n in /0. bedeutet das dann, dass fgets quasi jedes wort einzeln speichert und nicht nen kompletten satz inklusive leerzeichen?
und könntest du mir das hier
[cpp]memset(eingabe, 0x0, size*sizeof(struct musiktitel)); while (fread(&tmp, sizeof(struct musiktitel), 1, datei) > 0 && i<size) { eingabe[i++] = tmp; }[/cpp]
nochmal gaaaanz langsam punkt für punkt in worten erklären, was da passiert?
ich steig da immernochnicht durch.. was bedeutet zb 0x0?
sry wenn ich mich da zugegebenermaßen etwas dümmlich anstelle, aber wurde vor 4 wochen das erste mal mit c konfroniert...
danke und grüße
julian
-
julian06 schrieb:
also wenn ich das jetzt richtig verstanden habe, wandelt die funktion remove_nextline alle /n in /0. bedeutet das dann, dass fgets quasi jedes wort einzeln speichert und nicht nen kompletten satz inklusive leerzeichen?
Zum Einen, ja es wandelt jedes \n in \0 um. (Das ist ein
Back-Slash kein normaler Slash!!)
Zum Anderen, nein, lies dir mal die Beschreibung (man-page) von fgets
durch. Die Funktion liest solange Zeichen ein, bis es ein \n, das Ende
EOF gefunden hat oder der Buffer (size-1) voll ist.julian06 schrieb:
und könntest du mir das hier
memset(eingabe, 0x0, size*sizeof(struct musiktitel)); while (fread(&tmp, sizeof(struct musiktitel), 1, datei) > 0 && i<size) { eingabe[i++] = tmp; }
Das sollte eigentlich in deinem Buch beschrieben sein. 0x0 bedeutet einfach
nur 0. Ich habe es nur in hex-Schreibweise angegeben.
Die memset-Zeile initialisiert den Speicher eingabe mit lauter Nullen.
i++ ist das selbe wie i=i+1;
fread() liefert die Anzahl der erfolgreich gelesenen Datensätze zurück.
Da hier immer nur einer eingelesen wird, sollte da immer eine 1 zurück kommen,
es sei denn, die Datei ist komplett eingelesen, dann kommt eine 0 zurück.
i<size passt auf, dass nicht mehr Datensätze eingelesen werden, als Speicher
zur Verfügung steht.
Ach ja, der Datensatz wird von fread in tmp gespeichert.
So noch Fragen?Vielleicht solltest du dir das Buch auch mal durchlesen. Das ist alles
Standardcode, nichts besonderes. Und vielleicht ist ein Schinken zum
Programmieren lernen nicht besonders empfehlenswert. Ich hätte mir ein
Anfängerbuch gekauft: Vielleicht den Kerningham, Ritchie; "The C Programming
Language";
-
die sache mit fgets ist mir jetzt klar. hab newline mit nem leerzeichen verwechselt...peinlich...
aber nochmal zur zweiten funktion:
warum schreibst du das denn in hexa? hat das vorteile oder ist das nur so aus ner laune heraus?^^
du schreibst, dass mit hilfe von memset der speicher "eingabe" komplett mit nullen gefüllt wird. der speicher eingabe wird also sozusagen komplett entlehrt?fread() liefert die Anzahl der erfolgreich gelesenen Datensätze zurück.
verstehe.
Da hier immer nur einer eingelesen wird, sollte da immer eine 1 zurück kommen,
es sei denn, die Datei ist komplett eingelesen, dann kommt eine 0 zurück.das heißt, es wird ein eintrag nach dem anderen aus datei gelesen und in temp registriert, bis size erreicht ist? wobei jedes mal wenn temp "angesprochen" wird, i um eins erhöht wird?
die größe von size wiederum wird hierdurch definiert?
size*sizeof(struct musiktitel)
Das ist alles
Standardcode, nichts besonderes.für dich ja. für mich nein.^^
grüße und danke
julian
-
Hi,
ich habe da hex genommen, weil ich dort einfacher die Bitbelegung
ablesen kann. Hat sonst keinen anderen Hintergrund.das heißt, es wird ein eintrag nach dem anderen aus datei gelesen und in
temp registriert, bis size erreicht ist? wobei jedes mal wenn temp
"angesprochen" wird, i um eins erhöht wird?Fast: es wird in tmp zwischen- und anschließend unter
eingabe[i] dauerhaft gespeichert. Die Schleife bricht entweder ab, wenn
es keine Datensätze mehr in der Datei zu lesen gibt, oder wenn size (=100)
viele Datensätze gelesen wurden.Hier vielleicht mal der Code ohne irgendwelche Abkürzungen:
static int datei_lesen(struct musiktitel *eingabe, int size) { struct musiktitel tmp; int i, gelesen; FILE *datei=fopen("musik_db.txt", "rb"); memset(eingabe, 0x0, size*sizeof(struct musiktitel)); i = 0; gelesen = fread(&tmp, sizeof(struct musiktitel), 1, datei); while (gelesen > 0 && i<size) { eingabe[i] = tmp; i = i+1; gelesen = fread(&tmp, sizeof(struct musiktitel), 1, datei); } return i; }
Ist der nun verständlicher?
die größe von size wiederum wird hierdurch definiert?
Nein, size gibt an, wieviele Elemente für eingabe vorgesehen wird.
Diese Zahl darf nicht überschritten werden und sollte die gleiche
sein, wie in Zeile 60:struct musiktitel eingabe[100]; /*<-- size = 100*/
wie du in zeile 68 sehen kannst:
size = datei_lesen(eingabe, 100);
size*sizeof(struct musiktitel)
berechnet die benötigten Bytes für eingabe.
So, nun sollten aber alle Fragen beantwortet sein, oder?
-
ja super vielen dank nochmal.
deine mühe hat mir wirklich sehr geholfen.
grüße julian