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 der while(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 man strtol .
    Oder wenn dir das zu kompliziert ist auch sscanf(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


Anmelden zum Antworten