2016-04-22 20 views
1

テキストファイルの各行を配列に保存しようとしています。 彼らは方法私はそれをやっているし、これまで正常に動作しますが、このです:テキストファイルの各行を配列に格納する

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++; 
} 

私のテキストファイルは、私がこれまでのforループ

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

でとても良いアクセスしていますが40行を持っています。私の問題は、私は配列のサイズを定義することです 40行を持つテキストファイルです。私は行を数えて、サイズを定義しようとしましたが、私はセグメンテーションフォールトを得ています。

私のアプローチ:

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]; 

任意のアイデア?

+1

ポインタ配列自体のメモリを 'malloc'で割り当てます。上限に達すると、' realloc'でそれを拡張し、制限を更新します... –

+2

私たちはこれに対して在庫がありますか?それは約尋ねられる。 3x /日。 –

+0

@kata aside: 'fgets'は、' line'の最後に 'newline'を入力したままにしておいてください。 –

答えて

1

この問題にアプローチする多くの方法があります。静的な2D配列またはchar(例:char lines[40][50] = {{""}};)を宣言するか、またはという型の配列char [50]へのポインタを宣言します。これはおそらく動的割り当てにとって最も簡単です。そのアプローチでは、1つの割り当てだけが必要です。定数MAXL = 40MAXC = 50で、あなたは単に必要があります:あなたが行われてい

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

は、すべてを行う必要が駒を置くfree (lines);です:fgets各ラインを読む

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

の簡単な作業です

#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; 
} 

注:一緒に、あなたのような何かを行うことができますます毎回fgetsで行全体が読み取られたかどうかを確認する必要があります。 (ファイル内に38文字以上の長い行があるとします)。 ヌル終了の文字で上書きする前に、*p'\n'であるかどうかをチェックすることによって行います。 (例えば、if (*p != '\n') { int c; while ((c = getchar()) != '\n' && c != EOF) {} })。これにより、現在の行の残りの文字ではなく、次の行がfgetsで始まることが保証されます。(: Iが読み取りループ以下n = i;を割り当てるための必要性を排除するためにniからの読み出しループカウンタを変更注)

は、次のような何かを行うことができ、チェックを含めます。

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 */ 
    } 

残りの行を破棄しても、配列の長さを超えて残すかはあなた次第です。ただし、常にチェックすることをお勧めします。 (私の例入力中のテキストの行は、長い行の可能性がなかったので、下記の17文字に制限されていますが、一般的に行の長さを保証することはできません。

例入力

$ 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 

使用例/出力

$ ./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' 

は今例えば、コードの長さのチェックが含まれており、入力に長い行を追加します。

$ cat dat/40lines+long.txt 
line of text - 1 
line of text - 2 
line of text - 3 + 123456789 123456789 123456789 123456789 65->| 
line of text - 4 
... 

プログラムを再実行すると、ファイル内の長い行から保護されていることを確認できます。動的にあなたがあなたのファイルの行数が不明があり、lines40のあなたの最初の割り当てに達した場合、その後、あなたは追加の行を読み続けるために行う必要があるすべてがあるlines

の再割り当て


realloclinesのストレージ。例えば:あなたのファイルにありますどのように多くの行にそれを問題ではありませんあなたの全体のファイルが読み込まれるまで

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 */ 
     } 
    } 

は今、あなたは単にlinesを再割り当て続けるか、メモリが不足します。 (注:現在のコードは2回再配分のlinesの現在のメモリを再割り当てあなたが同じくらいか、あなたが好きなように少しを追加するのは自由ですたとえば、あなたは、単に40複数行毎の時間を割り当てることmaxl + 40を割り当てることができ

。。。

例入力

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

使用例/出力

$ ./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' 

質問がある場合はそれを見て、私に知らせてください。

+0

私のテキストファイルに40行以上がある場合はどうしますか? :/ – kata

+0

あなたはすでにwhile(n

+0

'40'以上を読んでほしいと言っているなら、 'realloc'行を続けてください。私は例を追加します。 –

1

私は、上記の正確なコードをテストして、行数(改行文字を数えて)、1000行を超えるファイル、いくつかの行4000文字を取得しました。問題はそこにありません。 segフォールトは、それぞれのラインバッファにメモリを割り当てる方法が原因である可能性があります。短いバッファに長い行を書き込もうとしている可能性があります。 (おそらく私はあなたの記事でそれを逃したが、行の長さを扱った場所を見つけることができませんでした)

文字列をファイルに格納するためにメモリを割り当てるときに便利な2つの機能は、行数とファイルの最大行長です。これらは配列charの配列の作成に使用できます。

あなたはfgets(...)にループすることにより、行数と最も長い行の両方を取得することができます(あなたのテーマのバリエーションを、本質的fgetsは改行を見つけるせ)

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 50より大きい長さの行は、改行され、少なくとも2行として読み込まれます: '改行 'を数えると、これは分かりません。 –

+0

@WeatherVane - このコメントをここに投稿しましたか? – ryyker

+1

はい、あなたの最初のパラが原因です。 –

関連する問題