[C] Auslesen (zeilenweise) einer Datei, splitten, speichern -> komische Zeichen
-
Hi zusammen,
Ich bin gerade im Begriff C zu lerne und ich versuche im Moment aus einer Textdatei Daten zu verarbeiten. Dabei geht es jeweils eine Zeile einzulesen, in Abschnitte zu trennen und in ein "struct" zu schreiben.
Die Datensätze sind wie folgt aufgebaut (jede Zeile soll 1 Datensatz werden):
Vorname;Nachname;Beruf;Alter;
Bisher habe ich folgenden Quellcode (Spoiler Funktion scheint es hier nicht zu geben?):
#include <stdio.h> #include <string.h> #include <stdlib.h> typedef struct Datenbank { char Vorname[100]; char Nachname[100]; char Beruf[200]; char Alter[10]; }kunden_t; daten_auslesen_zerlegen() { FILE *dateizeiger; char buffer[500]; // Zwischenspeicher für die ausgelesenen Strings der Text-Datei int sep_pos[10]; // Seperator Position => Wo ist ';' int sep_cur=0; // Zähler für den aktuellen gefundenen Seperator int i; kunden_t daten; /// Initialisierung der Variablen mit 0 for(i=0;i<sizeof(buffer);i++) { buffer[i]=0; } sep_pos[10] = 0; sep_cur = 0; i = 0; /// Datei öffnen und Zeilenweise auslesen dateizeiger = fopen("daten.txt","r"); fgets(buffer, sizeof(buffer), dateizeiger); printf("Länge des eingelesenen Strings im Buffer: %d\n\n",strlen(buffer)); /// Positionen der Seperatoren im eingelesenen String herausfinden for(i=0;(i<strlen(buffer)) && (buffer[i] != '\n');i++) { if(buffer[i] == ';') { sep_pos[sep_cur] = i; sep_cur++; printf("Sep_Cur: %d\n",sep_cur); // Überprüfung ob die Anzahl der Seperatoren stimmt } } /// String zerlegen und in "struct" speichern memcpy(daten.Vorname,&buffer[0],sep_pos[0]); printf("\nLänge des 1. Teils des Strings: %d\n\n",strlen(daten.Vorname)); // Überprüfen ob die Länge des Stringteils überhaupt stimmen kann printf("Teil 1 des Strings: %s\n",daten.Vorname); memcpy(daten.Nachname, &buffer[sep_pos[0]+1], sep_pos[1]-sep_pos[0]-1); printf("Teil 2 des Strings: %s\n",daten.Nachname); memcpy(daten.Beruf, &buffer[sep_pos[1]+1], sep_pos[2]-sep_pos[1]-1); printf("Teil 3 des Strings: %s\n",daten.Beruf); memcpy(daten.Alter, &buffer[sep_pos[2]+1], sep_pos[3]-sep_pos[2]-1); printf("Teil 4 des Strings: %s\n",daten.Alter); printf("\n\n\n"); return 0; } int main() { int eingabe; do { printf("\t\tMen\x81\n\n\n"); printf("0 Programm beenden.\n"); printf("1 Daten aus Datei auslesen\n"); scanf("%d",&eingabe); printf("\n\n"); switch(eingabe) { case 1: { daten_auslesen_zerlegen(); } break; } }while(eingabe != 0); return 0; }
HINWEIS: Wenn ihr den Code nehmt, müsst ihr euch im selben Verzeichnis eine Textdatei "daten.txt" erstellen, wo ihr z.B. das hier eintragt:Hans;Dieter;Bauunternehmer;36;
Das Auftrennen, etc. klappt auch, nur immer wenn ich das Programm ausführe, kommt es bei der Ausgabe der Teile des Strings (1.,2.,3.,4.) immer dazu, dass hinter den Einträgen noch so komische Zeichen angezeigt werden, die in der Text Datei so gar nicht drinstehen. Diese werden auch z.B. bei der Überprüfung der Länge des 1. Teils des Strings mit berücksichtigt, obwohl die wie gesagt gar nicht in der Textdatei, bzw. in dem einzulesenden String vorhanden sind.
Wenn ihr das Programm einfach mal ausführt, werdet ihr das sehen.
Allerdings bin ich nicht in der Lage den Fehler zu finden, und bräuchte dabei mal Unterstützung.
ZUSATZ:
Was mir aufgefallen ist, dass sich die Menge der "Zusatzzeichen" dann ändert, wenn ich die Werte der char Arrays m struct bzw. die Größe des char Arrays "Buffer" auf andere Werte setzte. Kann diese Phänomen leider nicht deuten.Für Hilfe wäre ich sehr dankbar
. Aber bitte so formulieren, dass ichs verstehe
(bin ja noch nicht so lange dabei).
-
Strings sind in C null-terminiert. Das bedeutet, ein char-Array, als String interpretiert, ist so lang, wie es Zeichen ungleich 0 enthält.
Konkret bedeutet das, dass du dafür sorgen musst, dass nach dem eigentlichen Inhalt des Strings noch ein Null-Byte angehängt wird. Etwa
memcpy(daten.Vorname,&buffer[0],sep_pos[0]); daten.Vorname[sep_pos[0]] = '\0';
Oh, und es wäre vermutlich eine gute Idee, sich gleich zu Anfang anzugewöhnen, auf die Länge der verfügbaren Buffer zu achten. In einen char[100] passen 99 Zeichen und ein Null-Byte, du solltest dementsprechend prüfen, dass du nicht mehr hinein schreibst. Beispielsweise
int len = sep_pos[0] < 99 ? sep_pos[0] : 99; memcpy(daten.Vorname, &buffer[0], len); daten.Vorname[len] = '\0';
Alternativ darf ich dir vielleicht die Funktionen strtok und strncpy aus der Standardbibliothek ans Herz legen.
-
Ich nicht.
Du liest nur die erste Zeile aus deiner Datei, dürfte wohl nicht beabsichtigt sein.
Du prüfst nicht den Rückgabewert von fopen.
Du vergisst fclose.
Die Initialisierung von buffer ist hier komplett überflüssig.
-
Du liest nur die erste Zeile aus deiner Datei, dürfte wohl nicht beabsichtigt sein.
Du prüfst nicht den Rückgabewert von fopen.Das ich nur die 1. Zeile prüfe, ist hier schon beabsichtigt, da ich einfach nur das Prinzip verstehen will, und das nicht unnötig komplizieren will.
Das mit dem Rückgabewert fällt quasi in dieselbe Kategorie. Ich will einfach nur eine vorhandene Zeile in einer Datei verarbeiten, ohne mich im Moment mit den Sonderfällen beschäftigen zu müssen. Zwar muss dann eine vorgegebene Datei vorhanden sein (was zumindest bei Fragen hier im Forum suboptimal ist), aber dafür muss ich mich im Moment nicht um eine Funktion kümmern, die etwas in eine Datei schreibt (diese neu anlegt, etc.). Mir gehts im Moment nur um den Ausleseprozess selbst.
Die Vorgehensweise ist für die "alten Hasen" bestimmt unerträglich^^, aber jeder hat mal neu angefangen.Die Initialisierung von buffer ist hier komplett überflüssig.
Kann du (oder jmd.) mir erklären, warum? Ich hab das eigentlich nur eingebaut um eventuelle Fehler auszuschließen. Hat den besagten Fehler zwar nicht beeinflusst, aber ich habsvergessen wieder rauszunehmen.
Konkret bedeutet das, dass du dafür sorgen musst, dass nach dem eigentlichen Inhalt des Strings noch ein Null-Byte angehängt wird.
Das muss ich machen, da memcpy nicht automatisch ein '\0' anhängt oder?
Das mit der Nullterminierung hatte ich zwar schon in anderen Programm verwendet, aber in diesem zusammenhang wieder vergessen.
Aber bekanntlich lernt man ja aus Fehlern.
es wäre vermutlich eine gute Idee, sich gleich zu Anfang anzugewöhnen, auf die Länge der verfügbaren Buffer zu achten. In einen char[100] passen 99 Zeichen und ein Null-Byte, du solltest dementsprechend prüfen, dass du nicht mehr hinein schreibst
In meinem Beispiel ist das mit der Länge ja noch nicht kritisch, aber du hast Recht. Besser ist es, sich gleich so etwas nicht falsch anzugewöhnen.
...Funktionen strtok und strncpy ...
Strtok() hab ich schon erfolgreich in einem kleineren Übungsprgoramm verwendet. In diesem Fall habs ichs aber nicht hinbekommen, die einzelnen Tokens in verschiedene Teile des Structs zu speichern. Ist bestimmt ganz einfach, aber da ich in der Hinsicht nicht weiterkam, hab ichs auf nem anderen Weg probiert. Ist mein Lösungsansatz prinzipell schlechter? Hätt da gern ne Rückmeldung, da ich wie gesagt im Moment noch sehr stark am rumprobieren bin.
Strcpy() kannte ich bis jetzt nicht, aber im Moment erschließt sich mir nicht ganz, wie ich das für mein Programm anwendet könnte. Könntest du mir da evt. nen Tipp geben
.
Schonmal vielen Dank bis hierhin für die Tipps Ich werd mich jetzt erstmal mit der Nullterminierung beschäftigen.
Meiner
-
Zum leidigen Thema strtok siehe z.B. http://www.c-plusplus.net/forum/viewtopic-var-t-is-275443-and-highlight-is-.html
Initialisieren brauchst du hier nicht, da du die famose Funktion fgets benutzt, welche neben einem Pufferüberlaufsschutz auch immer ein '\0' anhängt.
Das ganze Geraffel mit ';' brauchst du auch nicht, wenn du wie hier wohl eine konstante Anzahl von mit ';' getrennten Feldern benutzt:#define MAXKD 100 int z = 0; kunden_t daten[MAXKD]; ... while( fgets(buffer, 500, dateizeiger) ) if( z < MAXKD ) if( 4==sscanf(buffer,"%99[^;];%99[^;];%199[^;];%9[^;];",daten[z].Vorname,daten[z].Nachname,daten[z].Beruf,daten[z].Alter) ) ++z;
Hier im Beispiel habe ich der Einfachheit halber mal für daten ein Array benutzt, in der Praxis und zur Stackschonung nimmt man natürlich malloc/free.
-
Wutz schrieb:
"%99[;];%99[;];%199[;];%9[;];"
-
Danke für die Antworten.
Bevor ich aber dsa jetzige Konzept aufgebe, wollt ich nocheinmal das mit der Terminierung versuchen.
Wenn man als Beispiel nimmt:
Hans;Dieter;Bauunternehmer;36;
Ich habe die Terminierung wie folgt eingefügt:
[cpp] /// String zerlegen und in "struct" speichern memcpy(daten.Vorname,&buffer[0],sep_pos[0]); memcpy(daten.Nachname, &buffer[sep_pos[0]+1], (sep_pos[1]-sep_pos[0])-1); memcpy(daten.Beruf, &buffer[sep_pos[1]+1], (sep_pos[2]-sep_pos[1])-1); memcpy(daten.Alter, &buffer[sep_pos[2]+1], (sep_pos[3]-sep_pos[2])-1); /// Terminierungszeichen einfügen daten.Vorname[sep_pos[0]]='\0'; daten.Nachname[(sep_pos[1])-(sep_pos[0])]='\0'; daten.Beruf[sep_pos[2]-sep_pos[1]]='\0'; daten.Alter[sep_pos[3]-sep_pos[2]]='\0'; [/cpp]
Das funktioniert aber nicht. Trage ich hingegen (für das vorliegende Bsp.) die Werte ohne allg. Formel ein, dann funktioniert alles:
[cpp] /// Terminierungszeichen einfügen daten.Vorname[sep_pos[0]]='\0'; daten.Nachname[6]='\0'; daten.Beruf[14]='\0'; daten.Alter[2]='\0'; [/cpp]
Also muss die Formel sep_pos[x]-sep_pos[y] falsch sein. Den Fehler darin seh ich aber nicht... Jemand ne Idee?