Csv Datei in C einlesen, strtok
-
Hi, ich möchte gerne eine csv Datei in C einlesen. Die Daten die ich einlese sind genau so formatiert:
10000 07/01/1986 OPTIMUM MANUFACTURING INC OMFGA -2.56250 . 1.00000
10000 08/01/1986 OPTIMUM MANUFACTURING INC OMFGA -2.50000 . 1.00000Ich verwende als Delimiter ein Leerzeichen.
Ich bekomme es hin die Daten einzulesen. Allerdings habe ich ein Problem die dritte Spalte einzulesen. Also diesen String: OPTIMUM MANUFACTURING INC
Denn so wie ich aktuell diesen Text reinlese, liest es mir nur das erste Wor ein: OPTIMUM . Klar da dieser dann wieder mit einem Leerzeichen getrennt ist.Meine Frage: Wie bekomme ich es hin den Text OPTIMUM MANUFACTURING INC als einen String in name2 einzulesen?
Hier wäre meine Funktion dazu:
while(fgets(line, 200, fp )!= NULL) { sp = strtok(line, " "); permno = atoi(sp); sp = strtok(NULL, " "); strcpy(date, sp); sp=strtok(NULL, " "); strcpy(name2, sp); printf("%d %s %s\n",permno, date, name2); }
-
Das ist kein gültes CSV. Das Trennzeichen (hier wohl ein Leerzeichen) darf nicht Bestandteil der Werte der Datenfelder sein.
Aktuell hat kein CSV Parser der Welt die chance das so zu parsen wie du das möchtest.
Für einen Parser besteht deine "CSV" Datei aus 9 Datenfeldern, welche jeweils durch ein Leerzeichen getrennt sind
z.b. 10000 07/01/1986 OPTIMUM MANUFACTURING INC OMFGA -2.56250 . 1.00000- "10000"
- "07/01/1986"
- "OPTIMUM"
- "MANUFACTURING"
- "INC"
- "OMFGA"
- "-2.56250"
- "."
- "1.00000"
Du hast jetzt nur folgende Möglichkeiten:
Ändere das Trennzeichen für die Datenfelder (z.b. ';')
-
Sieht aus wie eine typische "CSV"-Datei von Kunden, mit denen man abgesprochen hat, dass man selbstverständlich CSV lesen kann, aber nur wenn das Trennzeichen nicht im String vorkommt oder ordentlich gequotet ist. Wobei - eigenlich müsste sich auch noch das Datumsformat ab und an ändern... /s
Du hast hier glücklicherweise nur ein Feld mit variabler Länge. Die ersten beiden Datenfelder sind klar. Dann nimmst du die hinteren 4 Felder. Und alles, was dann in der Mitte übrig bleibt, ist dein Name.
-
Sowas macht man nicht mit strtok, sondern mit fgets+sscanf.
Ist denn "OPTIMUM MANUFACTURING INC" und vor allem "OMFGA" immer konstant?
Wenn ja, läuft das auf einen 2Zeiler hinaus.
Wie lässt sich denn "OPTIMUM MANUFACTURING INC" beschreiben? Immer 2 Leerzeichen, immer exakt dieselbe Länge...?
-
@Wutz sagte in Csv Datei in C einlesen, strtok:
Wie lässt sich denn "OPTIMUM MANUFACTURING INC" beschreiben? Immer 2 Leerzeichen, immer exakt dieselbe Länge...?
So wie ich die Frage verstanden habe: beliebiger String (mit beliebig vielen Leerzeichen), aber ohne Zeilenumbrüche. Und das OMFGA ist irgendein anderer Identifier (String ohne Leerzeichen/Zeilenumbrüche).
Wenn man auch noch manchmal Zeilenumbrüche ungequotet in den Daten hat, freue ich mich immer besonders
-
[https://onlinegdb.com/sPJUPsPm6](ANSI C konformes Lösungsbeispiel)
-
@Wutz: Wieso macht man dies nicht mit strtok, sondern mit fgets+sscanf?
"OPTIMUM MANUFACTURING INC" und "OMFGA" sind nicht konstant, diese ändern sich.Danke, muss mir deinen Code nochmals ansehen.
Ich habe nun dennoch eine Möglichkeit gefunden strings aneinanderzuhängen, mit strcat.Aktuell kann ich die Daten mit meinen Code einlesen bis zu der Vorletzten floating zahl.
Die letzten beiden Strings möchte ich als floating Zahl einlesen. Allerdings lese ich da aktuell
0.00000 ein, warum weiß ich noch nicht.Dort wo der Vorletzte Punkt ist stehen manchmal Zahlen und manchmal nur der Punkt. Etwa so:
10000 07/01/1986 OPTIMUM MANUFACTURING INC OMFGA -2.56250 . 1.00000
10000 08/01/1986 OPTIMUM MANUFACTURING INC OMFGA -2.50000 . 1.00000
10000 09/01/1986 OPTIMUM MANUFACTURING INC OMFGA -2.50000 0.3 1.00000Hier mein gesamter Code:
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> FILE *read_file(char* filename) { FILE *fptr; if ((fptr = fopen(filename,"r")) == NULL) { printf("Error! opening file"); exit(1); } return fptr; flose(fptr); } int main(void) { int permno; float prc; char date[100]; char name[100]; char name2[100]; char name3[100]; char name4[100]; float number1, number2; FILE *fp = read_file("neu.csv"); char line[200]; char *sp; while(fgets(line, 200, fp )!= NULL) { sp = strtok(line, " "); permno = atoi(sp); sp = strtok(NULL, " "); strcpy(date, sp); sp=strtok(NULL, " "); strcpy(name, sp); sp=strtok(NULL, " "); strcpy(name2, sp); sp=strtok(NULL, " "); strcpy(name3, sp); strcat(name, name2); strcat(name, name3); sp=strtok(NULL, " "); strcpy(name4, sp); sp = strtok(NULL, ""); prc = atof(sp); sp = strtok(NULL, " "); number1 = atof(sp); sp = strtok(NULL, " "); number2 = atof(sp); printf("%d %s %s %s %f %f %f\n",permno, date, name, name4, prc, number1, number2); } return 0; }
-
Das Format ist der Horror.
Woher kommt denn diese "CSV" Datei?
Kann man das nicht ändern, dass als trenner z.b. ";" verwendet wird?
Das würde das ganze komplett vereinfachen
-
@jasmin89 Die ganze strtok-Orgie kann man mit einem sscanf-Aufruf erledigen.
strtok verändert den Ursprungsstring und ist nicht reentrant bzw. Thread-safe.
atof() nutzt man nicht mehr. Man kann keine Fehler erkennen. Dafür gibt es strtod().
In Zeile 65 hast du einen Leerstring als Delimiter.
-
Das Format ist echt schlimm. Ich versuche die Datei gerade umzubearbeiten. Verwende dazu EmEditor
und versuche anstatt ein Leerzeichen ein Komma einzufügen. Verwende EmEditor da die Datei 12GB groß ist, mal schauen ob das mein PC handeln kann.@DirkB Danke für den Hinweiß.
-
@jasmin89 sagte in Csv Datei in C einlesen, strtok:
"OPTIMUM MANUFACTURING INC" und "OMFGA" sind nicht konstant, diese ändern sich.
...
10000 07/01/1986 OPTIMUM MANUFACTURING INC OMFGA -2.56250 . 1.00000
10000 08/01/1986 OPTIMUM MANUFACTURING INC OMFGA -2.50000 . 1.00000
10000 09/01/1986 OPTIMUM MANUFACTURING INC OMFGA -2.50000 0.3 1.00000Den Teil bis zum Anfang von "OPTIMUM MANUFACTURING INC" kannst du ja eindeutig parsen, das sollte einfach genug sein. Den abschliessenden Teil kannst du ebenso eindeutig parsen, indem du z.B. von Rechts anfängst Leerzeichen zwischen den Tokens zu suchen. Damit kannst du dir dann den String "OPTIMUM MANUFACTURING INC OMFGA" rausschneiden. Wenn du weiter nichts über die Teile weisst aus denen sich dieser String zusammensetzt, dann hast du ein Problem.
Wenn du aber z.B. wüsstest dass der "OMFGA" Teil nie Leerzeichen enthält, dann kannst du auch den von Rechts parsen. Und "OPTIMUM MANUFACTURING INC" ist einfach das was in der Mitte übrig bleibt.
Und: ich würde hier weder scanf noch strtok verwenden, sondern den Code um die Zeilen in die einzelnen Teile zu zerlegen selbst schreiben. Mit möglichst vielen Checks drinnen um Fehler bzw. falsche Annahmen zu erkennen. Bei einer 12 GB grossen Eingabedatei wird es sonst vermutlich kaum möglich sein zu erkennen wenn man falsche Annahmen getroffen hat.
-
@hustbaer
das macht mein o.g. Code so; sscanf ist unerlässlich für komfortable Fehlerchecks innerhalb der Zeile.
Und dem Code ist es auch egal, ob es 12 GB oder 12 TB Daten sind.
-
Ich muss nochmals etwas fragen. Ich verwende den Code von Wutz, funktioniert auch prima. Nur möchte ich nun die while Schleife abbrechen sobald eine bestimmte Taste gedrückt wird. Dies möchte ich aus verschiedenen Gründen so machen. Ich frage die Tastatureingabe mit kbhit() ab, das funktioniert auch. Nur wenn ich das so aktuell mache dauert das ewig lange (12TB Datei). Habe dazu im Programm die Zeit gemessen. Zum Vergleich ohne die Abfrage mit kbhit() läuft das Programm ca. 200 Sekunden. Mit der Tastaturabfrage läuft das Programm sehr lange. Ich habe da nach 2000 Sekunden abgebrochen.
Meine Frage: Kennt Ihr eine andere Möglichkeit wie ich die while Schleife verlassen kann, mit einemTastendruck ohne kbhit()? Habe dazu nix nennenswertes gefunden. So wie ich es sehe braucht diese Tastaturabfrage sehr lange.
#include <stdio.h> #include <string.h> #include <time.h> #include <conio.h> char *strrev(char *str) { char *p1, *p2; if (! str || ! *str) return str; for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) { *p1 ^= *p2; *p2 ^= *p1; *p1 ^= *p2; } return str; } int main() { clock_t time_start = clock(); FILE *x=fopen("daily.txt", "r"); char s[1000],a[1000],b[1000],c[1000],d[1000],e[1000],f[1000],g[1000]; while( fgets(s,1000,x) ) { if(kbhit()) { int key = getch(); if(key == 'y') { break; } } int n1,n2; char t[1000]; if( 2==sscanf(s,"%s%s%n",a,b,&n1) ) if( 4==sscanf(strrev(strcpy(t,s)),"%s%s%s%s%n",g,f,e,d,&n2) ) if( 1==sscanf(s+n1+1,"%[^\n]",c) ) c[strlen(c)-n2]=0;printf("%s%s%s%s%s%s%s\n",a,b,c,strrev(d),strrev(e),strrev(f),strrev(g)); } fclose(x); clock_t time_end = clock(); clock_t duration = time_end - time_start; double zeit = (double)duration / CLOCKS_PER_SEC; printf("zeit: %lf\n", zeit); getch(); return 0; }
-
@jasmin89
kbhit&Co sind selbst die Ursache für die Verlangsamung und außerdem auch nicht standardkonform.
Was willst du denn mit deinem Abbruch erreichen?
Dass du kaputte Daten hast und nochmal neu starten musst?!
Du kannst die Schleifendurchläufe zählen und zB nur bei jedem 100. die Tastatur abfragen, vielleicht hilft das.
-
@Wutz Hallo, danke für den Tipp. Ich werde es versuchen. Ich möchte, während die Datei eingelesen wird nach einem bestimmten Wort suchen und diese Zeile printen. Sobald die Zeile gefunden wurde will ich das mit einer Taste abbrechen. Das Problem ist dass mehrere Zeilen das Wort beinhalten und nur ich selbst als „Mensch“ kann beurteilen ob die richtige Zeile gefunden wurde. Damit wird halt nicht die ganze Datei dursucht sondern nur so weit wie nötig
Nur aus interesse: Verwendet die Windows Suche eigentlich auch die Funktion khbit()? Denn da kann ich ja auch mit Esc abbrechen. Oder ist die Windows Suche „intelligenter“ aufgebaut? Die Windows Suche müsste ja in C/C++ programmiert sein.
-
@jasmin89 sagte in Csv Datei in C einlesen, strtok:
Oder ist die Windows Suche „intelligenter“ aufgebaut?
Ja.
Die verwendet die Windows-API und da laufen Dinge parallel.Die Windows Suche müsste ja in C/C++ programmiert sein.
Kann auch C# sein, oder ...
-
@jasmin89 sagte in Csv Datei in C einlesen, strtok:
@Wutz Hallo, danke für den Tipp. Ich werde es versuchen. Ich möchte, während die Datei eingelesen wird nach einem bestimmten Wort suchen und diese Zeile printen. Sobald die Zeile gefunden wurde will ich das mit einer Taste abbrechen. Das Problem ist dass mehrere Zeilen das Wort beinhalten und nur ich selbst als „Mensch“ kann beurteilen ob die richtige Zeile gefunden wurde. Damit wird halt nicht die ganze Datei dursucht sondern nur so weit wie nötig
Wenn du das Programm komplett abbrechen willst, dann kannst du auch einfach Ctrl+C drücken. Das sollte funktionieren ohne dass du extra dafür etwas programmierst.
Nur aus interesse: Verwendet die Windows Suche eigentlich auch die Funktion khbit()? Denn da kann ich ja auch mit Esc abbrechen. Oder ist die Windows Suche „intelligenter“ aufgebaut? Die Windows Suche müsste ja in C/C++ programmiert sein.
Die Suche im Windows Explorer verwendet sicher nicht
khbit()
, da Explorer kein Konsolenprogramm ist. Den Tastatur-Input bekommt Explorer vermutlich wie die meisten anderen Windows-Anwendungen über Window-Messages.
-
Danke für die Hilfe, hier wird weitergeholfen. Ich rufe nun nur alle 1000 Iterationen mal khbit() auf und das funktioniert prima. Hier noch mein Codeschnipsel als Lösung.
int count = 0; while( fgets(s,1000,x) ) { if(count>1000) { if(kbhit()) { int key = getch(); if(key == 'y') { break; } } count=0; } count++; . . . . }
-
@jasmin89 Kreative Einrückungen.
for (int count = 0; fgets(buffer, 1000, input_file); ++count) { if (count > 1000 && kbhit()) { int key = getch(); if (key == 'y') break; count = 0; } // do something with buffer }
-
@Swordfish sagte in Csv Datei in C einlesen, strtok:
if (count > 1000 && kbhit()) {
Da wird aber ab 1000 immer das langsame kbhit() aufgerufen, wenn keine Taste gedrückt wurde.
if (count > 1000 && !((count = 0)) && kbhit()) { // ja, count wird auf 0 gesetzt. Doppelklammern, damit der Compiler keine Warnung gibt.
PS: Das funktioniert wegen der https://de.wikipedia.org/wiki/Kurzschlussauswertung
-
Achje, ne, dann lieber das 2.
if
lassen.Oder, wenn's denn wirklich unbedingt super-kompakt sein muss:
if (!(count & 0x3FFu) && kbhit() && getch() == 'y') break;
Mit unsigned count, weil sonst UB bei Überlauf.