ファイルの内容を行毎に配列して取得する

ファイルの内容を行毎に全部取得して二次元配列にするプログラムです。

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

#define LINEMAX 100
#define LINESMAX 5000

int readlines (FILE *fh,char **lines,int size) {
    int linenum = 0;
    size_t len;
    size_t linelen = 0;
    char str[LINEMAX];
    char *p = NULL;
    int add = 1;
    
    while( fgets(str,sizeof(str),fh) != NULL ) {
        linelen += len = strlen(str);
        add = (str[len-1] == '\n') ? 1 : 0;
        p = (char *)realloc(p,linelen+1);
        if ( p == NULL ) {
            return EOF;
        }
        strcpy_s(p+linelen-len,len+1,str);
        if ( add ) {
            if ( linenum == size ) { break; }
            lines[linenum++] = p;
            linelen = 0;
            p = NULL;
        }
    }
    
    return linenum;
}

int main (void) {
    char *lines[LINESMAX];
    int linenum;
    int i;
    
    linenum = readlines(stdin,lines,LINESMAX);
    if ( linenum == EOF ) {
        puts("読み込みエラー");
        return 1;
    }
    
    for(i=0;i<linenum;i++){
        printf("::%s",lines[i]);
    }
    
    return 0;
}

こんな感じ実装してみました。

一行の長さはどれだけ長くてもreallocで拡張するので問題なく動きます。

だたしこのままだと総行数は5000行までしか読み込めません。

実際のファイルは何行のファイルになるか分からないのでそこも考慮しないといけないとういことで行数を保持するlines変数もmallocでメモリ確保するように変更してみました。

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

#define LINEMAX 100
#define LINESMAX 128

int readlines (FILE *fh,char ***lines,int size) {
    int linenum = 0;
    size_t len;
    size_t linelen = 0;
    char str[LINEMAX];
    char *p = NULL;
    int add = 1;
    
    *lines = (char **)malloc(size*sizeof(int));
    
    while( fgets(str,sizeof(str),fh) != NULL ) {
        linelen += len = strlen(str);
        add = (str[len-1] == '\n') ? 1 : 0;
        p = (char *)realloc(p,linelen+1);
        if ( p == NULL ) {
            return EOF;
        }
        strcpy_s(p+linelen-len,len+1,str);
        if ( add ) {
            if ( linenum == size ) {
                size <<= 1;
                *lines = (char **)realloc(*lines,sizeof(int)*size);
            }
            (*lines)[linenum++] = p;
            linelen = 0;
            p = NULL;
        }
    }
    
    
    return linenum;
}

int main (void) {
    char **lines = NULL;
    int linenum;
    int i;
    
    linenum = readlines(stdin,&lines,LINESMAX);
    if ( linenum == EOF ) {
        puts("読み込みエラー");
        return 1;
    }
    
    for(i=0;i<linenum;i++){
        printf("::%s",lines[i]);
    }
    
    return 0;
}

このようにLINESMAXを超えたらreallocで元の配列の2倍の値で拡張していきます。

ただし、倍倍されていくので確保したものの、使わない無駄なメモリを確保してしまう可能性もあります。

そう考えると1要素ずつメモリ増やした方がいいのかもしれません。こういう場合、どうするのが正解なんでしょうかね、ちょっとわからないです。