Daten einlesen



  • olli.ke schrieb:

    Ich habe allerdings in einer Zeile mehrere Integer werte. Und deren Anzahl variert, ist aber bekannt.
    Das ist mein Problem.

    ... auch das ist kein wirkliches Problem. Also passen wir das Program mal kurz an (als Idee, ungetestet):

    int *intarr;
    int nintarr;
    int ival;
    int len;
    char inbuf[1000];
    ...
    intarr = NULL;      /* pointer initialisieren */
    nintarr = 0;        /* counter initialisieren */
    ...
    while (fgets(inbuf,sizeof(inbuf),inputFile) != NULL)
        {
        len = strlen(inbuf);
        if (len > 0)
            {
            len--;
            inbuf[len] = (char) 0;  /* fgets() hängt LF an => löschen */
            }
        while ((iret = sscanf(inbuf,"%d",&ival)) > 0)
            {
            if (intarr == NULL)
                {
                intarr = (int *) malloc((nintarr + 1) * sizeof(int));
                if (intarr == NULL) => .... fehler
                }
            else
                {
                intarr = (int *) realloc((void *) intarr, (nintarr + 1) * sizeof(int));
                if (intarr == NULL) => .... fehler
                }
            intarr[nintarr++] = ival;    
            }
        ...
        /*
        jetzt sind alle Integer einer Zeile eingelesen und können verarbeitet 
        werden, z.B.:
        for (ix = 0; ix < nintarr; ix++)
            printf("Wert #%02d: %d\n",ix,intarr[ix]);
        */
        ...
        if (intarr != NULL)
            {                           /* am Ende der while-Schleife bzw. Zeile: */
            free((void *) intarr);      /* nach Verarbeitung Speicher freigeben */
            intarr = NULL;              /* pointer initialisieren */
            nintarr = 0;                /* counter initialisieren */
            }
        }
    

    Das Ganze kann man, wie gesagt, auch noch optimieren (z.B. immer 100 Elemente auf einen Schlag alloziieren). Auch kann man die einmal alloziierten Elemente für Folgezeilen wiederverwenden. Aber oben soll erst einmal nur das Prinzip verdeutlicht werden.

    Ich mach das immer gerne mit malloc()/free(), wenn ich nicht genau weiß, wie groß die maximale Obergrenze der einzulesenden Elemente ist. realloc() kann man gut gebrauchen, wenn man die Anzahl nachträglich erhöhen will. Damit wird der alte Inhalt in den neu erhaltenen Speicherbereich umkopiert, d.h. die Werte bleiben erhalten.

    Weiß man die Maximalgrenze allerdings bereits zur Compile-Zeit, so kann man auch ein statisches Array vorsehen, z.B. "int intarr[100];". (Das ist dann aber trivial und macht nicht wirklich Spaß 😢

    Weiß man zur Laufzeit des Programms, wieviel Elemente maximal pro Zeile vorkommen können, so kann man ein malloc() am Anfang jeder Zeile machen.

    Das obige Beispiel läßt sich natürlich auch so umgestalten, dass man die ganze Datei in ein quasi zweidimensionales Integer Array einliest (genau genommen: ein Array von Arrays von Integer-Werten). Hier kann man dann beide Dimensionen variabel gestalten, sprich dynamisch alloziieren (das ist dann nicht mehr trivial und bringt wirklich Spaß ;-).

    Wenn man pro Zeile gemischte Datentypen hat, z.B. int und float, so kann man jeweils ein Array mit union-Typen vorsehen (dann wird's noch spaßiger :-))



  • Das macht spass?!?
    Okay, das ist schon fast das was ich brauche.

    Meine einlese datei sieht so aus:

    1 2 3 546 64 76 75 8 68 86
    3 6 8 23 1 3 5 8 9 9
    ...

    also 10 Integerwerte durch " "getrennt. Die Anzahl=10 ist bekannt,
    In jeder Zeile die selbe Anzahl unterschiedliche Werte.
    Auch die Anzahl der Zeile ist bekannt. Ich will alles dann in einem zweidimensionalen Array haben.

    das Problem ist, dass bei nächsten Aufruf des Programms z.B. 12 Integerwerte sein können.



  • olli.ke schrieb:

    also 10 Integerwerte durch " "getrennt. Die Anzahl=10 ist bekannt,
    In jeder Zeile die selbe Anzahl unterschiedliche Werte.
    Auch die Anzahl der Zeile ist bekannt. Ich will alles dann in einem zweidimensionalen Array haben.
    das Problem ist, dass bei nächsten Aufruf des Programms z.B. 12 Integerwerte sein können.

    Ich hab Dich gewarnt, es wird erheblich komplexer...

    int **intarrpp;     /* Array von Arrays, 1. Dimension */
    int *introwp;       /* Array von int, 2. Dimension */
    int nrow;           /* Anzahl rows/Zeilen */
    int ncolumn;        /* Anzahl columns/Spalten */
    int ival;
    int len;
    int rowix;
    int colix;
    char inbuf[1000];
    ...
    intarrpp = NULL;    /* pointer initialisieren */
    nrow = 0;           /* counter/Anzahl initialisieren */
    ncolumn = 0;        /* counter/Anzahl initialisieren */
    
    ...
    while (fgets(inbuf,sizeof(inbuf),inputFile) != NULL)
        {
        len = strlen(inbuf);
        if (len > 0)
            {
            len--;
            inbuf[len] = (char) 0;  /* fgets() hängt LF an => löschen */
            }
        else
            {
            /* Speicher für 1. Dimension alloziieren */
            if (intarrpp == NULL)
                {
                intarrpp = (int **) malloc((nrow + 1) * sizeof(int *));
                if (intarrpp == NULL)  => .... fehler
                intarrpp[nrow] = NULL;
                }
            else
                {
                intarrpp = (int **) realloc((void *) intarrpp,(nrow + 1) * sizeof(int *));
                if (intarrpp == NULL)  => .... fehler
                intarrpp[nrow] = NULL;
                }
            }
    
        colix = 0;
        while ((iret = sscanf(inbuf,"%d",&ival)) > 0)
            {
            /* Speicher für 2. Dimension alloziieren */
            if (nrow == 0)    /* 1. Zeile? */
                {
                if (intarrpp[nrow] == NULL)
                    {
                    introwp = (int *) malloc((ncolumn + 1) * sizeof(int));
                    if (introwp == NULL) => .... fehler
                    intarrpp[nrow] = introwp;
                    }
                else
                    {
                    introwp = (int *) realloc((void *) intarrpp[nrow], (ncolumn + 1) * sizeof(int));
                    if (introwp == NULL) => .... fehler
                    intarrpp[nrow] = introwp;
                    }
                if (introwp != NULL)        
                    ncolumn++;
                }
            else if (colix == 0) /* nur einmal malloc bei Zeile 2 .. n, ncolumn wurde bei 1. Zeile berechnet */
                {
                introwp = (int *) malloc(ncolumn * sizeof(int));
                if (introwp == NULL) => .... fehler
                intarrpp[nrow] = introwp;
                }
            /* ... und eingelesenen Wert abspeichern */
            if ((introwp != NULL) && (colix < ncolumn))
                introwp[colix++] = ival;
            else  => ... fehler    
            }
        nrow++;     /* Erhöhen Anzahl Zeilen */
        }
    
    ...
    
    /*
    jetzt sind alle Integer der Datei eingelesen und können verarbeitet
    werden, z.B.:
    for (rowix = 0; rowix < nrow; rowix++)
        {
        introwp = intarrpp[rowix];
        for (colix = 0; colix < ncolumn; colix++)
            printf("intarr[%02d:%02d]: %d\n",rowix,colix,introwp[colix]);
        }  
    */
    
    ...
    
    /* am Ende: Freigabe Speicher */
    for (rowix = 0; rowix < nrow; rowix++)
        {
        introwp = intarrpp[rowix];
        if (introwp != NULL)
            free((void *) introwp);
        intarrpp[rowix] = NULL;
        }
    if (intarrpp != NULL)
        {
        free((void *) intarrpp;
        intarrpp = NULL;
        }
    nrow = 0;         /* counter/Anzahl initialisieren */
    ncolumn = 0;      /* counter/Anzahl initialisieren */
    
    ...
    

    Zum Spaß habe ich ein paar Unzulänglichkeiten eingebaut, damit Du es nicht zu einfach hast (es würde mich wundern, wenn das Programm ohne Tests auf Anhieb läuft 😉



  • olli.ke schrieb:

    Die Anzahl=10 ist bekannt,
    In jeder Zeile die selbe Anzahl unterschiedliche Werte...
    das Problem ist, dass bei nächsten Aufruf des Programms z.B. 12 Integerwerte sein können.

    😮



  • #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
     int **intarrpp;     /* Array von Arrays, 1. Dimension */ 
     int *introwp;       /* Array von int, 2. Dimension */ 
     int nrow;           /* Anzahl rows/Zeilen */ 
     int ncolumn;        /* Anzahl columns/Spalten */ 
     int ival; 
     int len; 
     int rowix, iret; 
     int colix; 
     char inbuf[1000]; 
    int main(void){
     FILE *inputFile;
     inputFile = fopen("./daten","r");
     intarrpp = NULL;    /* pointer initialisieren */ 
     nrow = 2;           /* counter/Anzahl initialisieren */ 
     ncolumn = 13;        /* counter/Anzahl initialisieren */ 
    
     while (fgets(inbuf,sizeof(inbuf),inputFile) != NULL) { 
         len = strlen(inbuf); 
         if (len > 0) 
             { 
             len--; 
             inbuf[len] = (char) 0;  /* fgets() hängt LF an => löschen */ 
             } 
         else 
             { 
             /* Speicher für 1. Dimension alloziieren */ 
             if (intarrpp == NULL) 
                 { 
                 intarrpp = (int **) malloc((nrow + 1) * sizeof(int *)); 
                 if (intarrpp == NULL)  printf("Fehler\n");
                 intarrpp[nrow] = NULL; 
                 } 
             else 
                 { 
                 intarrpp = (int **) realloc((void *) intarrpp,(nrow + 1) * sizeof(int *)); 
                 if (intarrpp == NULL)  printf("Fehler\n");
                 intarrpp[nrow] = NULL; 
                 } 
             } 
    
         colix = 0; 
         while ((iret = sscanf(inbuf,"%d",&ival)) > 0) 
             { 
             /* Speicher für 2. Dimension alloziieren */ 
             if (nrow == 0)    /* 1. Zeile? */ 
                 { 
                 if (intarrpp[nrow] == NULL) 
                     { 
                     introwp = (int *) malloc((ncolumn + 1) * sizeof(int)); 
                     if (introwp == NULL) printf("Fehler\n");
                     intarrpp[nrow] = introwp; 
                     } 
                 else 
                     { 
                     introwp = (int *) realloc((void *) intarrpp[nrow], (ncolumn + 1) * sizeof(int)); 
                     if (introwp == NULL) printf("Fehler\n");
                     intarrpp[nrow] = introwp; 
                     } 
                 if (introwp != NULL)         
                     ncolumn++; 
                 } 
             else if (colix == 0) /* nur einmal malloc bei Zeile 2 .. n, ncolumn wurde bei 1. Zeile berechnet */ 
                 { 
                 introwp = (int *) malloc(ncolumn * sizeof(int)); 
                 if (introwp == NULL) printf("Fehler\n");
                 intarrpp[nrow] = introwp; 
                 } 
             /* ... und eingelesenen Wert abspeichern */ 
             if ((introwp != NULL) && (colix < ncolumn)) 
                 introwp[colix++] = ival; 
             else  printf("Fehler\n");
             } 
         nrow++;     /* Erhöhen Anzahl Zeilen */ 
         } 
    
     //jetzt sind alle Integer der Datei eingelesen und können verarbeitet 
     // werden, z.B.: 
     for (rowix = 0; rowix < nrow; rowix++) 
         { 
         introwp = intarrpp[rowix]; 
         for (colix = 0; colix < ncolumn; colix++) 
             printf("intarr[%02d:%02d]: %d\n",rowix,colix,introwp[colix]); 
         }
    
     /* am Ende: Freigabe Speicher */ 
     for (rowix = 0; rowix < nrow; rowix++) 
         { 
         introwp = intarrpp[rowix]; 
         if (introwp != NULL) 
             free((void *) introwp); 
         intarrpp[rowix] = NULL; 
         } 
     if (intarrpp != NULL) 
         { 
         free((void *) intarrpp); 
         intarrpp = NULL; 
         } 
     nrow = 0;         /* counter/Anzahl initialisieren */ 
     ncolumn = 0;      /* counter/Anzahl initialisieren */ 
    }
    

    gibt keine Fehler, nur dann, leider bekomme ich dann

    Speicherzugriffsfehler und sonst nix
    😕



  • olli.ke schrieb:

    ... gibt keine Fehler, nur dann, leider bekomme ich dann
    Speicherzugriffsfehler und sonst nix

    ... das war die angesprochene eingebaute "Unzulänglichkeit" 😉

    Doch im Ernst: Deine Änderungen

    nrow = 2;           /* counter/Anzahl initialisieren */
     ncolumn = 13;        /* counter/Anzahl initialisieren */
    

    waren leider kontraproduktiv. Lass beides auf 0 als Anfangsinitialisierung. Der Code bestimmt selbst Anzahl Zeilen (nrow) und Anzahl Spalten (ncolumn). Genial, was? (zumindest die Idee, ob es tatsächlich auch so funktioniert ;-?



  • Auch damit hatte ich es schon probiert, nur leider keine Verbesserung, selber Fehler



  • olli.ke schrieb:

    Auch damit hatte ich es schon probiert, nur leider keine Verbesserung, selbe Fehler

    ... häufiger Fehler bei malloc-Nutzung, wenn irgendwo Speicher fehlt und Referenzen ins Nirwana zeigen. Du musst ausprobieren, bis wohin Dein Programm kommt, entweder im Debugger oder zur Not auch mit printf's. Wenn Du auf 'nem Unix/Linux bist, kannst Du auch mit dem gdb den core untersuchen.

    Ich bin leider ab gleich erst einmal offline ...

    Viel Erfolg beim debuggen (das schaffst Du)

    edit: Mit der Vorinitialisierung auf konkrete Werte geht's auf keinen Fall!!

    edit: Wie gesagt, ist komplexer ... und gibt auch komplexere Fehler!!!



  • Das Problem ist das sscanf, das liest immer nur den ersten wert aus, nie den 2.



  • Mist,

    while ((iret = sscanf(inbuf,"%d",&ival)) > 0)
    

    gibt eine klassische Totschleife, da immer nur die erste Zahl des Strings gelesen wird und nicht im String auf die nächste Zahl weitergesprungen wird ....

    Da muss noch dran gearbeitet werden 😉



  • Das Problem ist also aus einem String, eine bestimmte Anzahl von Integer werten auszulesen.



  • wir müssten also einen String einlesen, bis ein Freizeichen kommt, alles vorher in einen Intwert umwandeln, das Leerzeichen entfernen, u.s.w.????



  • olli.ke schrieb:

    das Leerzeichen entfernen, u.s.w.????

    Nein brauchst du nicht. Setze einfach den pointer weiter.

    const char *p = inbuf;
    while (sscanf(p,"%d",&ival) > 0) {
      // Mache was mit deinem ival
      for (; *p && isdigit(*p); ++p)
        ;
    }
    


  • inputFile = fopen("./daten","r");
    
    while (fgets(inbuf,sizeof(inbuf),inputFile) != NULL) {
         	char *p = inbuf;
    	sscanf(p,"%d",&ival);
    	printf("%d",ival);
    	for (; *p && isdigit(*p); p++);
    	while (sscanf(p,"%d",&ival) > 0) { 
    		if(isblank(*p)){
    			p++;
    
    			}
    		else{
    			printf(" %d",ival);
    			for (; *p && isdigit(*p); p++);
    			}
    
     		}
    	printf("\n_________________\n");
    	}
    

    so funktioniert es, nur gibt er das letzte Zeichen der Datei 2x aus, woher kommt das?



  • ... so jetzt gehts (jetzt getestet unter Linux):

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
     int **intarrpp;     /* Array von Arrays, 1. Dimension */
     int *introwp;       /* Array von int, 2. Dimension */
     int nrow;           /* Anzahl rows/Zeilen */
     int ncolumn;        /* Anzahl columns/Spalten */
     int ival;
     int len;
     int rowix, iret;
     int colix;
     char inbuf[1000];
     int bufpos;
    
    int main(void){
     FILE *inputFile;
     inputFile = fopen("./daten","r");
     intarrpp = NULL;    /* pointer initialisieren */
     nrow = 0;           /* counter/Anzahl initialisieren */
     ncolumn = 0;        /* counter/Anzahl initialisieren */
    
     while (fgets(inbuf,sizeof(inbuf),inputFile) != NULL) {
         len = strlen(inbuf);
         if (len > 0)                /* nur für nicht leere Zeilen */
             {
             len--;
             inbuf[len] = (char) 0;  /* fgets() hängt LF an => löschen */
    
    	 /* Speicher für 1. Dimension alloziieren */
    	 if (intarrpp == NULL)
    	     {
    	     intarrpp = (int **) malloc((nrow + 1) * sizeof(int *));
    	     if (intarrpp == NULL)  printf("Fehler\n");
    	     intarrpp[nrow] = NULL;
    	     }
    	 else
    	     {
    	     intarrpp = (int **) realloc((void *) intarrpp,(nrow + 1) * sizeof(int *));
    	     if (intarrpp == NULL)  printf("Fehler\n");
    	     intarrpp[nrow] = NULL;
    	     }
    
    	 colix = 0;
    	 bufpos = 0;                       /* positions index initialisieren */
    	 while (isspace(inbuf[bufpos]))    
    	     bufpos++;                     /* führende space zeichen überspringen */
    	 while ((iret = sscanf(&inbuf[bufpos],"%d",&ival)) > 0)
    	     {
                 while (isdigit(inbuf[bufpos]))
    		bufpos++;                  /* positions index über zahl weitersetzen */
    	     while (isspace(inbuf[bufpos]))
    		bufpos++;		   /* space zeichen überspringen */
    	     /* Speicher für 2. Dimension alloziieren */
    	     if (nrow == 0)    /* 1. Zeile? */
    		 {
    		 if (intarrpp[nrow] == NULL)
    		     {
    		     introwp = (int *) malloc((ncolumn + 1) * sizeof(int));
    		     if (introwp == NULL) printf("Fehler\n");
    		     intarrpp[nrow] = introwp;
    		     }
    		 else
    		     {
    		     introwp = (int *) realloc((void *) intarrpp[nrow], (ncolumn + 1) * sizeof(int));
    		     if (introwp == NULL) printf("Fehler\n");
    		     intarrpp[nrow] = introwp;
    		     }
    		 if (introwp != NULL)        
    		     ncolumn++;
    		 }
    	     else if (colix == 0) /* nur einmal malloc bei Zeile 2 .. n, ncolumn wurde bei 1. Zeile berechnet */
    		 {
    		 introwp = (int *) malloc(ncolumn * sizeof(int));
    		 if (introwp == NULL) printf("Fehler\n");
    		 intarrpp[nrow] = introwp;
    		 }
    	     /* ... und eingelesenen Wert abspeichern */
    	     if ((introwp != NULL) && (colix < ncolumn))
    		 introwp[colix++] = ival;
    	     else  printf("Fehler\n");
    	     }
    	 nrow++;     /* Erhöhen Anzahl Zeilen */
    	 }
         }
    
     //jetzt sind alle Integer der Datei eingelesen und können verarbeitet
     // werden, z.B.:
     for (rowix = 0; rowix < nrow; rowix++)
         {
         introwp = intarrpp[rowix];
         for (colix = 0; colix < ncolumn; colix++)
             printf("intarr[%02d:%02d]: %d\n",rowix,colix,introwp[colix]);
         }
    
     /* am Ende: Freigabe Speicher */
     for (rowix = 0; rowix < nrow; rowix++)
         {
         introwp = intarrpp[rowix];
         if (introwp != NULL)
             free((void *) introwp);
         intarrpp[rowix] = NULL;
         }
     if (intarrpp != NULL)
         {
         free((void *) intarrpp);
         intarrpp = NULL;
         }
     nrow = 0;         /* counter/Anzahl initialisieren */
     ncolumn = 0;      /* counter/Anzahl initialisieren */
    }
    

    Ich habe einen Positionsindex benutzt, um im Inputbuffer weiterzugehen. Im Prinzip geht es auch mit einem Pointer, wie von @javaner vorgeschlagen, aber für den ersten Ansatz wird es mit einem Index deutlicher (ich würde persönlich auch mit einem Pointer arbeiten 😉

    Der Speicherzugriffsfehler war bedingt durch das erste "else" nach der Prüfung "if (len > 0) ...". Konnte man simple durchs Debuggen herausbekommen.... Es gab dann schlicht kein Speicher für intarrpp 😢

    Sorry, aber den Fehler in Deinem Code habe ich jetzt nicht weiter überprüft, aber ich schätze es liegt daran, dass nach dem Einlesen eines int-Wertes jeweils mittels isdigit() und isspace() weiterpositioniert werden muss. Sieht stark danach aus, dass das bei Dir nicht immer korrekt gemacht wird (den Fehler hatte ich zunächst auch, einige Zahlen wurden doppelt ausgegeben 😉

    ... so jetzt weiß ich auch, warum ich für das Einlesen von Strings innerhalb eines Inputbuffers normalerweise eine eigene uralte selbstgeschriebene "Standard"-Routine benutze (und nicht sscanf), die automatisch nach jeder eingelesenen Zeichenkette weiterpositioniert 😉

    Und habe ich nicht recht, dass es ein wenig komplexer geworden ist?



  • benutz funktionen, dann wirds nicht so komplex.



  • c.rackwitz schrieb:

    benutz funktionen, dann wirds nicht so komplex.

    Du hast recht. Das ist eine ziemlich allgemein gehaltene Aussage, die fast immer gilt, aber nicht wirklich etwas zur konkreten Problemlösung beiträgt. In dieser allgemeinen Formulierung, kann man annehmen, Du meinst Standard-Bilbliotheksfunktionen. Um das klarzustellen: Es gibt keine Standardfunktionen zur konkreten Problemlösung, gemeint sind mit der obigen Formulierung selbst geschriebene Funktionen. Und ich gehe mal davon aus, Du meinst in der Formulierung nicht "komplex" sondern "übersichtlicher". Dann kann ich Dir Recht geben.

    Denn man kann hier geteilter Meinung sein, ob man das Einlesen von Werten aus einer Datei in ein 2-dimensionales Array weniger komplex mit Funktionen hinbekommt. Die Gesamtproblematik mit dynamisch erzeugten Arrays ist halt von Natur aus etwas komplexer als z.B. mit einem statischen 2-dimensionalen Array. Mir ging es primär um die Idee, darzustellen, wie die Problemstellung mit dynamisch erzeugten Arrays zu lösen ist (mit welchem Algorithmus und mit welchen Hilfsmitteln). Es ist jedem freigestellt, dass in einem - funktionierenden - Beispiel weniger komplex (nicht "weniger übersichtlicher") mit Hilfe von Funktionen darzustellen. Viel Spaß 😉


Anmelden zum Antworten