bestimmte Spalte einer CSV Datei einlesen
-
Hallo zusammen,
versuche mich gerade an einem C Programm, welches aus einer Datei (csv) nur die zweite Spalte einliest (am besten in ein Array). Habe derzeit folgenden Code, welcher mir jedoch die gesamte Datei in ein Array einliest.
#include <stdio.h> int main ( void ){ static const char filename[] = "1.txt"; FILE *file = fopen ( filename, "r" ); int i, j; char arra[128][128]; char line[128]; /* or other suitable maximum line size */ for(i=0; i<128; i++) for(j=0; j<128; j++) arra[i][2] = '\0'; for(i=0; i<128; i++) line[2] = '\0'; if ( file != NULL ){ i=0; while ( fgets ( line, sizeof line, file ) != NULL ) /* read a line */ { strcpy(arra[i], line); printf(&arra[i]); i++; } fclose ( file ); } else{ perror ( filename ); /* why didn't the file open? */ } return 0; }
Wie würde weiterhin ein Schleife aussehen, welche es erlaubt, aus allen Dateien innerhalb eines Ordners (alle Dateien haben den gleichen Aufbau) die zweite Spalte in ein gemeinsames Array einzulesen.
Vielen Dank für Eure Hilfe.
Gruß,
Silvi
-
Welchen Charakter wird zum separieren genommen? Komma oder Semikolon oder Tabulator oder Leerzeichen oder ...
Was steht den in den ersten beiden Spalten? (Fließkomma)-Zahlen oder Text
Auf welchem System soll das Programm übersetzt werden und laufen (Compiler und Betriebssystem)?
Und nimm besser die C/C++ (cpp) Tags statt Code. Dann wirds auch farbig und bunt.
-
Hallo DirkB,
entschuldige die fehlenden Informationen. Bin noch ein absoluter Laie in Sachen C. Als Trennzeichen wird das Komma verwendet. Die erste Spalte der CSV-Datei enthält MAC-Adressen der Form 00:00:00:00:00:00. Die zweite Spalte (welche ich benötige) enthält ganze Zahlen.
#include <stdio.h> int main ( void ){ static const char filename[] = "1.txt"; FILE *file = fopen ( filename, "r" ); int i, j; char arra[128][128]; char line[128]; /* or other suitable maximum line size */ for(i=0; i<128; i++) for(j=0; j<128; j++) arra[i][2] = '\0'; for(i=0; i<128; i++) line[2] = '\0'; if ( file != NULL ){ i=0; while ( fgets ( line, sizeof line, file ) != NULL ) /* read a line */ { strcpy(arra[i], line); printf(&arra[i]); i++; } fclose ( file ); } else{ perror ( filename ); /* why didn't the file open? */ } return 0; }
-
Um das in einem Programm machen zu können, brauchst du Nicht-C-Standard Funktionen, z.B. opendir/readdir o.ä.
-
Zum konvertieren der Werte nimmst du sscanf
Da liest du die Mac und dein Werte ein.
-
Hallo zusammen,
vielen Dank für Eure Antworten. Der Code sieht derzeit folgendermaßen aus:
#include <stdio.h> int main(void){ const char filename[] = "1.txt"; FILE *file = fopen(filename, "r"); if ( file ){ char line [ BUFSIZ ]; while ( fgets(line, sizeof line, file) ){ char substr[32], *ptr = line; int n; fputs(line, stdout); while ( *ptr ){ if ( sscanf(ptr, "%31[^,]%n", substr, &n) == 1 ){ ptr += n; puts(substr); } else{ puts("---empty field---"); } ++ptr; } } } else{ perror(filename); } return 0; }
Das einzulesende File sieht dabei so aus:
XXXX,XXXX,XXXX,XXXX,XXXX,XXXX 00:00:00:00:00:00,0,00/00,-00XXX,XXXX,XXXX
Die erste Zeile, welche aus Strings besteht, sollte ignoriert werden. Die folgenden Zeilen sind immer gleich aufgebaut. Aus diesen wird nur der jeweilige 2.INT Wert benötigt, damit mit diesem später weiter gerechnet werden kann. Der derzeitige Code listet die jeweilige Zeile auf und unterteilt sie dann in die jeweiligen Abschnitte. Wie kann ich es nun hinbekommen, dass nur der jeweilige zweite Wert aus jeder Zeile in ein gemeinsames Array oder Vektor gelesen wird.
Vielen Dank im Voraus!
Gruß
Silvi
-
Die erste Zeile solltest du mit
fgets
noch vor derwhile(fgets(line
einlesen.Die weiteren Daten kannst du mit
//Achte auf den Scope der folgenmden Variablen int zweiterIntWert[FELDGROESSE]; int ZeilenZaehler = 0; char colon; ... // Die ptr-Sache brauchst du da nicht if( sscanf(ptr, "%31[^,]%c%d", substr, &colon, &zweiterIntWert[ZeilenZaehler++]) < 3) { // MAC bis um Komma, das Komma und der IntWert
FELDGROESSE musst du festlegen (groß genug halt).
-
Silvi21 schrieb:
Wie kann ich es nun hinbekommen, dass nur der jeweilige zweite Wert aus jeder Zeile in ein gemeinsames Array oder Vektor gelesen wird.
Wenn Du nur den zweiten Wert aus einer Zeile brauchst, dann würde ich mir das Sscanf-Geraffel sparen. Es wird ja nur ein Wert nach dem ersten Komma gesucht. Wenn dieser Wert größer 0 ist, dann lautet mein Vorschlag:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { int* arr = NULL; int count=0; const char filename[] = "1.txt"; FILE *file = fopen(filename, "r"); if ( file ) { char line [ BUFSIZ ]; while ( fgets(line, sizeof line, file) ) { char* p = strchr(line,','); if (p == NULL) continue; // Zeile ohne 2. Spalte (z.B. Leerzeile) ++p; int i = atoi (p); if (i == 0) continue; // Wert ist kein Integer arr = (int*) realloc (arr,(count+1)*4); arr[count] = i; ++count; } } fclose (filename); for (int i=0; i<count; ++i) { printf ("arr[%i]: %i\n",i,arr[i]); } if (arr) free (arr); return 0; }
viele grüße
ralph
-
rkhb schrieb:
int i = atoi (p); if (i == 0) continue; // Wert ist kein Integer
Seit wann ist 0 kein Integer?
Mit
atoi
kannst du nicht festtellen, ob die Konvertierung fehlerhaft ist.
Um das festzustellen, nimmt manstrtol
.
Oder wenn dir das zu kompliziert ist auchsscanf(p, "%d", &Variable)
-
Hallo und vielen Dank für die Vorschläge. Bekomme leider beim Kompilieren vom Code folgenden Fehler:
cc -c changeChannel.c In function 'main': 23:5: error: 'for' loop initial declarations are only allowed in C99 mode 23:5: note: use option -std=c99 or -std=gnu99 to compile your code make: *** [changeChannel.o] Error 1
-
...habe das Problem selbst lösen können:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { int* arr = NULL; int count=0; int i; const char filename[] = "1.txt"; FILE *file = fopen(filename, "r"); if ( file ) { char line [ BUFSIZ ]; while ( fgets(line, sizeof line, file) ) { char* p = strchr(line,','); if (p == NULL) continue; // Zeile ohne 2. Spalte (z.B. Leerzeile) ++p; int i = atoi (p); if (i == 0) continue; // Wert ist kein Integer arr = (int*) realloc (arr,(count+1)*4); arr[count] = i; ++count; } } fclose (filename); for (i=0; i<count; ++i) { printf ("arr[%i]: %i\n",i,arr[i]); } if (arr) free (arr); return 0; }
-
Silvi21 schrieb:
Hallo und vielen Dank für die Vorschläge. Bekomme leider beim Kompilieren vom Code folgenden Fehler:
cc -c changeChannel.c In function 'main': 23:5: error: 'for' loop initial declarations are only allowed in C99 mode 23:5: note: use option -std=c99 or -std=gnu99 to compile your code make: *** [changeChannel.o] Error 1
for(int i = 0; i < irgendwas; i++)
ist nicht erlaubt.
int i; for(i = 0; ...)
so!
edit: zu spaet gesehen. Schön, dass es dir selbst aufgefallen ist!
-
Vielen Dank @rkhb,
dein Code funktioniert super. Wie kann man es jetzt noch hinbekommen, das alle Dateien mit der Endung *.txt aus einem Verzeichnis eingelesen werden? Die Dateien, die in diesem Verzeichnis liegen, haben alle den gleichen Aufbau wie die "1.txt".
Gruß,
Silvi
-
Silvi21 schrieb:
Wie kann man es jetzt noch hinbekommen, das alle Dateien mit der Endung *.txt aus einem Verzeichnis eingelesen werden? Die Dateien, die in diesem Verzeichnis liegen, haben alle den gleichen Aufbau wie die "1.txt".
Es sieht so aus, als arbeitest Du an einem POSIX-konformistischem System (Linux) mit einem POSIX-konformistischem Compiler (GCC). Also:
#include <stdio.h> #include <string.h> #include <dirent.h> int main () { struct dirent* dp; char* dir_path = "."; DIR* dir = opendir (dir_path); if (dir == NULL) { perror("OPENDIR fehlgeschlagen"); return 1; } while ( (dp=readdir(dir)) != NULL ) { char* p = strrchr (dp->d_name,'.'); if (p && strcmp(p,".txt") == 0) { printf ("%s\n",dp->d_name); } } closedir (dir); return 0; }
viele grüße
ralph
-
Hallo ralph,
vielen Dank für Deine Antwort. Dein Code listet mir alle Dateien im Verzeichnis auf, welche auf *.txt enden. Wie schafft man es, dass all diese Dateien so verarbeitet werden, dass von nur die INT-Werte der zweite Spalte von ihnen in ein Array geschrieben werden. Sozusagen eine Erweiterung des Deines ersten Codes.
Gruß,
Silvi
-
In Zeile 22 werden die Dateinamen ausgegeben.
An der Stelle kannst du dein bisheriges Einleseprogramm einpflegen.
Nicht einfach einfügen.Besser ist es, wenn du dein Einleseprogramm als Funktion schreibst und in Zeile 22 aufrufst.
-
Silvi21 schrieb:
Wie schafft man es, dass all diese Dateien so verarbeitet werden, dass von nur die INT-Werte der zweite Spalte von ihnen in ein Array geschrieben werden.
Ich bekomme langsam schwer den Eindruck, dass es sich um eine Semesterhausaufgabe handelt. Was will der Professor denn noch? Mittelwert? Summe? Umrechnung aus der Unixtime? Eintrag in eine relationale Datenbank?
Den Königsweg hat DirkB beschrieben. Das PRINTF dient als Platzhalter, um anzuzeigen, was zu diesem Zeitpunkt bekannt ist. Das kann man natürlich auch als Argument an eine Funktion übergeben.
Ich selbst bevorzuge immer schnelles Schließen (close, closedir) und unverzügliche Freigabe (free). Das bedeutet, dass bei offenen Dateien und offenen Verzeichnisse nur das Nötigste abgearbeitet bzw. erst einmal gespeichert wird. Weitere Aktionen erfolgen auf dem Speicher. Das sähe dann etwa so aus:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <dirent.h> int main () { int i,j; // für kurzfristige Zwecke, z.B. Laufvariablen struct dirent *dp; const char *dir_path = "."; DIR *dir = opendir (dir_path); if (dir == NULL) { perror("OPENDIR fehlgeschlagen"); return 1; } puts ("Dateien:"); char* dirs = NULL; int files_size = 0; int files_count = 0; while ( (dp=readdir(dir)) != NULL ) { char* p = strrchr (dp->d_name,'.'); if (p && strcmp(p,".txt") == 0) { int index = files_size; files_size += strlen(dp->d_name) + 1; char* d = realloc (dirs,files_size); if (d == NULL) { if (dirs) free (dirs); perror ("REALLOC fehlgeschlagen"); return 2; } dirs = d; strcpy (dirs+index,dp->d_name); ++files_count; } } closedir(dir); for (i=0, j=0; i<files_count; ++i) { printf ("%i %s\n",i,dirs+j); j += strlen (dirs+j) + 1; } puts(""); puts ("Integerwerte in der zweiten Spalte:"); int* integers = NULL; int integers_count = 0; for (i=0, j=0; i<files_count; ++i) { char* filename = dirs+j; FILE *file = fopen(filename, "r"); if ( file ) { char line [ BUFSIZ ]; while ( fgets(line, sizeof line, file) ) { char* p = strchr(line,','); if (p == NULL) continue; // Zeile ohne 2. Spalte (z.B. Leerzeile) ++p; int integer = atoi (p); if (integer == 0 && *p != '0') continue; // Wert ist kein gültiges Integer int* a = (int*) realloc (integers,(integers_count+1)*4); if (a == NULL) { if (dirs) free (dirs); perror ("REALLOC fehlgeschlagen"); return 2; } integers = a; integers[integers_count] = integer; ++integers_count; } fclose (file); j += strlen (dirs+j) + 1; } } if (dirs) free (dirs); for (i=0; i<integers_count; ++i) { printf ("integers[%i]:\t%i\n",i,integers[i]); } if (integers) free (integers); return 0; }
Das Auseinanderpfriemeln in Funktionen überlasse ich Dir.
viele grüße
ralph
-
Hallo ralph,
vielen Dank für Deine Hilfe. Dein Code funktioniert super. Bei der Aufgabe handelt es sich nicht um eine Hausaufgabe. Es ist ein kleines privates Projekt und ich versuche ein wenig C zu verstehen. Habe jedoch damit so meine Problemchen.
Wenn ich beispielsweise 8 Dateien mit je 10 Werten (in der zweiten Spalte) einlese, bekomme ich als Ausgabe 80 Arrays untereinander. Wie kann man jetzt mit diesen Werten weiter rechnen? An welcher Stelle vom Code muss man hierfür ansetzen? Würde gern zu jedem Wert die Häufigkeit des Auftretens anzeigen lassen. Weiterhin sollte jeder Wert nur einmal ausgegeben werden.Viele Grüße,
Silvi
-
Du hast ein Array* mit 80 Werten. Dieses Array heißt
integers
Und wie in Zeile 89 zu sehen ist kannst du darauf z.B mit integers[i] zugreifen.
Wenn du jetzt weitermachen willst, musst du deinen neuen Code in Zeile 91 einfügen. Nimm Dazu die Schleife von Zeile 87-90 als Vorbild.
Spätestens jetzt solltest du aber eigene Funktion nutzen, sonst wird das Ganze zu unübersichtlich.
Welchen Wertebereich haben denn deine Zahlen aus Spalte 2?
Wie/womit lernst du denn C?
*integers ist eigentlich kein Array sondern ein Zeiger auf einen Speicherbereich (der mitmalloc/realloc beschafft wurde) Der Unterschied spielt hier erstmal keine Rolle.
-
Hallo DirkB,
vielen Dank für Deine Anmerkungen. Versuche mich in C hiermit http://home.fhtw-berlin.de/~junghans/cref/index.html einzuarbeiten. Der Wertebereich der 2.Spalte liegt im Interval [1;14].
Viele Grüße,
Silvi