2016-04-22 15 views
1

Ich versuche, jede Zeile einer Textdatei in einem Array zu speichern. Sie so, wie ich es tun, und arbeiten so weit in Ordnung, ist dies: Speichern jeder Zeile einer Textdatei in einem Array

for(j = 0; j <= 39 ; j++) 
{ /*Do something to each line*/}. 

So weit so gut

char *lines[40]; 
char line[50]; 
int i = 0 ; 
char* eof ; 
while((eof = fgets(line, 50, in)) != NULL) 
{ 
    lines[i] = strdup(eof); /*Fills the array with line of the txt file one by one*/ 
    i++; 
} 

Meine Textdatei 40 Zeilen hat, die ich mit einem

for-Schleife am zugreifen. Mein Problem ist, dass ich die Größe des Arrays Zeilen für die eine Textdatei definieren, die 40 Zeilen hat. Ich habe versucht, die Zeilen zu zählen und dann die Größe zu definieren, aber ich bekomme Segmentierung Fehler.

Mein Ansatz:

int count=1 ; char c ; 
for (c = getc(in); c != EOF; c = getc(in)) 
    if (c == '\n') // Increment count if this character is newline 
     count = count + 1; 
printf("\nNUMBER OF LINES = %d \n",count); 

char* lines[count]; 

Irgendwelche Ideen?

+1

Ordnen Sie Speicher für das Zeigerfeld selbst mit 'malloc' zu, wenn es Ihr Limit erreicht, erweitern Sie es mit' realloc' und aktualisieren Sie das Limit ... –

+2

Haben wir eine Bestandsantwort dafür? Es wird ca. gefragt. 3x/Tag. –

+0

@kata zur Seite: Denken Sie daran, dass 'fgets' eine 'newline' am Ende von' line' behalten soll. –

Antwort

1

Es gibt viele Möglichkeiten, dieses Problem zu nähern. Deklarieren Sie entweder ein statisches 2D-Array oder ein char (z. B. char lines[40][50] = {{""}};) oder deklarieren Sie einen -Zeiger auf ein Array vom Typ char [50], das wahrscheinlich am einfachsten für die dynamische Zuweisung ist. Mit diesem Ansatz benötigen Sie nur eine einzige Zuweisung. Bei konstantem MAXL = 40 und MAXC = 50, müssen Sie einfach:

char (*lines)[MAXC] = NULL; 
... 
lines = malloc (MAXL * sizeof *lines); 

lesen jede Zeile mit fgets ist eine einfache Aufgabe:

while (i < MAXL && fgets (lines[i], MAXC, fp)) {... 

Wenn Sie fertig sind, alles, was Sie tun müssen, ist free (lines); Setzt man die Stücke

#include <stdio.h> 
#include <stdlib.h> 

enum { MAXL = 40, MAXC = 50 }; 

int main (int argc, char **argv) { 

    char (*lines)[MAXC] = NULL; /* pointer to array of type char [MAXC] */ 
    int i, n = 0; 
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin; 

    if (!fp) { /* valdiate file open for reading */ 
     fprintf (stderr, "error: file open failed '%s'.\n", argv[1]); 
     return 1; 
    } 

    if (!(lines = malloc (MAXL * sizeof *lines))) { /* allocate MAXL arrays */ 
     fprintf (stderr, "error: virtual memory exhausted 'lines'.\n"); 
     return 1; 
    } 

    while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */ 
     char *p = lines[n];     /* assign pointer */ 
     for (; *p && *p != '\n'; p++) {}  /* find 1st '\n' */ 
     *p = 0, n++;       /* nul-termiante */ 
    } 
    if (fp != stdin) fclose (fp); /* close file if not stdin */ 

    /* print lines */ 
    for (i = 0; i < n; i++) printf (" line[%2d] : '%s'\n", i + 1, lines[i]); 

    free (lines); /* free allocated memory */ 

    return 0; 
} 

Anmerkung: zusammen, können Sie so etwas wie tun dir ein Ebenso sollte überprüft werden, ob die gesamte Zeile jedes Mal von fgets gelesen wurde. (Angenommen, Sie hatten eine lange Reihe von mehr als 38 Zeichen in der Datei). Dazu prüfen Sie, ob *p'\n' vor dem Überschreiben mit dem nullenden Zeichen ist. (z.B. if (*p != '\n') { int c; while ((c = getchar()) != '\n' && c != EOF) {} }). Dies stellt sicher, dass der nächste Lesevorgang mit fgets mit der nächsten Zeile statt mit den restlichen Zeichen in der aktuellen Zeile beginnt.

Um die Prüfung sind Sie etwas ähnliches wie die folgenden tun könnten (Anmerkung: ich i-n die Leseschleifenzähler geändert, um die Notwendigkeit für die Zuordnung n = i; nach der Leseschleife zu beseitigen).

while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */ 
     char *p = lines[n];     /* assign pointer */ 
     for (; *p && *p != '\n'; p++) {} /* find 1st '\n' */ 
     if (*p != '\n') {     /* check line read */ 
      int c; /* discard remainder of line with getchar */ 
      while ((c = fgetc (fp)) != '\n' && c != EOF) {} 
     } 
     *p = 0, n++;      /* nul-termiante */ 
    } 

Es liegt an Ihnen, ob Sie den Rest der Zeilen, die die Länge Ihres Arrays überschreiten, verwerfen oder behalten. Es ist jedoch eine gute Idee, immer zu überprüfen. (Die Textzeilen in meinem Beispiel Eingang unten auf 17 Zeichen beschränkt, so gibt es keine Möglichkeit, eine Linie lang war, aber Sie können in der Regel nicht die Zeilenlänge garantieren.

Beispiel Eingabe

$ cat dat/40lines.txt 
line of text - 1 
line of text - 2 
line of text - 3 
line of text - 4 
line of text - 5 
line of text - 6 
... 
line of text - 38 
line of text - 39 
line of text - 40 

Beispiel Verwendung/Output

$ ./bin/fgets_ptr2array <dat/40lines.txt 
line[ 1] : 'line of text - 1' 
line[ 2] : 'line of text - 2' 
line[ 3] : 'line of text - 3' 
line[ 4] : 'line of text - 4' 
line[ 5] : 'line of text - 5' 
line[ 6] : 'line of text - 6' 
... 
line[38] : 'line of text - 38' 
line[39] : 'line of text - 39' 
line[40] : 'line of text - 40' 

nun auch eine der Längenprüfung in Code und eine lange Leitung mit dem Eingang, zB:

Führen Sie das Programm erneut aus, und Sie können bestätigen, dass Sie jetzt vor langen Zeilen in der Datei geschützt sind, die Ihr sequentielles Lesen von Zeilen aus der Datei muckt.


dynamisch Neuverteilung lines

Wenn Sie eine unbekannte Anzahl von Zeilen in der Datei haben, und Sie Ihre anfängliche Zuteilung von 40 in lines erreichen, dann alles, was Sie tun müssen, lesen zusätzliche Leitungen zu halten ist realloc Speicher für lines. Zum Beispiel:

int i, n = 0, maxl = MAXL; 
    ... 
    while (fgets (lines[n], MAXC, fp)) {  /* read each line */ 
     char *p = lines[n];     /* assign pointer */ 
     for (; *p && *p != '\n'; p++) {}  /* find 1st '\n' */ 
     *p = 0;        /* nul-termiante */ 
     if (++n == maxl) { /* if limit reached, realloc lines */ 
      void *tmp = realloc (lines, 2 * maxl * sizeof *lines); 
      if (!tmp) {  /* validate realloc succeeded */ 
       fprintf (stderr, "error: realloc - virtual memory exhausted.\n"); 
       break;  /* on failure, exit with existing data */ 
      } 
      lines = tmp; /* assign reallocated block to lines */ 
      maxl *= 2;  /* update maxl to reflect new size */ 
     } 
    } 

Jetzt spielt es keine Rolle, wie viele Zeilen in der Datei sind, halten Sie einfach lines, bis die ganze Dateien gelesen wird Umschichtungen vorzunehmen, oder Sie laufen aus dem Speicher. (Anmerkung: zur Zeit ordnet der Code zweimal den aktuellen Speicher für lines auf jeder Umschichtung Sie sind frei, so viel hinzuzufügen, oder so wenig wie Sie möchten zum Beispiel Sie maxl + 40 zuordnen könnte einfach 40 jedes Mal mehr Zeilen zuzuordnen

...

Beispiel Eingabe

$ cat dat/80lines.txt 
line of text - 1 
line of text - 2 
... 
line of text - 79 
line of text - 80 

Beispiel Verwendung/Output

$ ./bin/fgets_ptr2array_realloc <dat/80lines.txt 
line[ 1] : 'line of text - 1' 
line[ 2] : 'line of text - 2' 
... 
line[79] : 'line of text - 79' 
line[80] : 'line of text - 80' 

Schauen Sie vorbei und lassen Sie mich wissen, wenn Sie irgendwelche Fragen haben.

+0

Was passiert, wenn meine Textdatei mehr als 40 Zeilen enthält? :/ – kata

+0

Sie sind bereits geschützt durch 'while (n

+0

Wenn Sie sagen, dass Sie mehr als "40" lesen möchten, dann "realloc" Zeilen und weitermachen. Ich füge ein Beispiel hinzu. –

1

Nebenbei habe ich den genauen Code getestet, den Sie oben zeigen, um Zeilenanzahl (durch das Zählen von Zeilenvorschubzeichen) zu erhalten, in einer Datei mit mehr als 1000 Zeilen und mit einigen Zeilen 4000 Zeichen lang. Das Problem ist nicht da. Die seg Fehler ist daher wahrscheinlich aufgrund der Art, wie Sie Speicher für jeden Zeilenpuffer zuweisen. Möglicherweise versuchen Sie, eine lange Zeile in einen kurzen Puffer zu schreiben. (vielleicht verpasste ich es in Ihrem Beitrag, konnte aber nicht finden, wo Sie Zeilenlänge adressiert?)

Zwei Dinge nützlich bei der Zuweisung von Speicher zum Speichern von Zeichenfolgen in einer Datei sind die Anzahl der Zeilen und die maximale Zeilenlänge in der Datei. Diese können verwendet werden, um das Array von char Arrays zu erstellen.

Sie können durch Schleifen auf fgets(...) sowohl Zeilenzahl und längste Linie erhalten: (eine Variation auf Ihrem Thema, im Wesentlichen fgets die Zeilenumbrüche finden lassen)

int countLines(FILE *fp, int *longest) 
{ 
    int i=0; 
    int max = 0; 
    char line[4095]; // max for C99 strings 
    *longest = max; 
    while(fgets(line, 4095, fp)) 
    { 
     max = strlen(line); 
     if(max > *longest) *longest = max;//record longest 
     i++;//track line count 
    } 
    return i; 
} 
int main(void) 
{ 
    int longest; 
    char **strArr = {0}; 
    FILE *fp = fopen("C:\\dev\\play\\text.txt", "r"); 
    if(fp) 
    { 
     int count = countLines(fp, &longest); 
     printf("%d", count); 
     GetKey(); 
    } 
    // use count and longest to create memory 
    strArr = create2D(strArr, count, longest); 
    if(strArr) 
    { 
     //use strArr ... 
     //free strArr 
     free2D(strArr, lines); 
    } 
    ......and so on 
    return 0; 
} 

char ** create2D(char **a, int lines, int longest) 
{ 
    int i; 
    a = malloc(lines*sizeof(char *)); 
    if(!a) return NULL; 
    { 
     for(i=0;i<lines;i++) 
     { 
      a[i] = malloc(longest+1); 
      if(!a[i]) return NULL; 
     } 
    } 
    return a; 
} 

void free2D(char **a, int lines) 
{ 
    int i; 
    for(i=0;i<lines;i++) 
    { 
     if(a[i]) free(a[i]); 
    } 
    if(a) free(a); 
} 
+0

@kata jede Zeile der Länge größer als 50 wird gebrochen und als mindestens 2 Zeilen gelesen: das Zählen der 'Newlines' wird Ihnen das nicht sagen. –

+0

@WeatherVane - wolltest du diesen Kommentar hier posten? – ryyker

+1

Ja, wegen Ihres ersten Abs. –

Verwandte Themen