ユーニックス総合研究所

  • home
  • archives
  • c-file-yomikomi-hairetsu-ni-kakuno

C言語でファイルを読み込み配列にデータを格納する

  • 作成日: 2024-03-22
  • 更新日: 2024-03-22
  • カテゴリ: C言語

C言語でファイルを読み込み、そのデータを配列に格納する方法を解説します。

C言語や他の言語を扱うYoutubeも公開しています。
興味がある方は以下のリンクからご覧ください。

Youtubeの当チャンネル

固定長の配列にデータを読み込む

固定長のデータの配列にファイル内容を読み込むサンプルは以下になります。

#include <stdio.h>  

enum {  
    DATA_LEN = 256,  // データの長さ  
};  

int main(void) {  
    // 読み込み先のデータ  
    char data[DATA_LEN + 1];  // + 1はナル文字用  

    // ファイルを開く  
    FILE *fin = fopen("sample.txt", "r");  
    if (fin == NULL) {  
        perror("fin");  
        return 1;  
    }  

    // ファイル内容を読み込む  
    int nread = fread(data, sizeof(data[0]), DATA_LEN, fin);  
    data[nread] = '\0';  // ナル文字を付加  

    // データ内容を出力  
    printf("data[%s]\n", data);  

    // ファイルを閉じる  
    fclose(fin);  

    return 0;  
}  

上記のコードを実行すると以下のような結果になります。

data[Hello, World!  
Good World!  
Goodbye, World!  
]  
#include <stdio.h>  

上記ではstdio.hをインクルードしています。printf関数やfopen関数を使う場合はこのヘッダーをインクルードします。

enum {  
    DATA_LEN = 256,  // データの長さ  
};  

上記ではenumDATA_LENという定数を定義しています。
DATA_LENはデータの保存先の配列の長さです。
ここではとりあえず256にしています。もっと大きなデータにした場合は適時値を変えてください。

    // 読み込み先のデータ  
    char data[DATA_LEN + 1];  // + 1はナル文字用  

上記では配列であるdataを定義しています。
配列の要素数はDATA_LEN + 1になります。
+ 1にしているのは配列の末尾に格納するナル文字のためです。
C言語では配列を文字列として扱う場合は配列の末尾にナル文字のスペースがないといけません。

    // ファイルを開く  
    FILE *fin = fopen("sample.txt", "r");  
    if (fin == NULL) {  
        perror("fin");  
        return 1;  
    }  

上記ではファイルをfopen関数で開いています。
fopen関数は第1引数のパスを第2引数のモードで開きます。

#include <stdio.h>  

FILE *fopen(const char *pathname, const char *mode);  

さきほどのコードではモードはrにしています。
これはテキストファイルを読み込む時のモードです。
バイナリにしたい場合はrbにします。

finがファイルポインタですが、これがNULLの場合はperror関数でエラーを出力します。

#include <stdio.h>  

void perror(const char *s);  

perror関数は引数の文字列を標準エラーに出力し、そのあとにerrnoを文字列にしたものを付加します。
errnoとはエラーを表すグローバル変数のことです。C言語ではこのerrnoでエラー処理することが多いです。

関連記事
C言語のerrnoの使い方: 初期化、文字列化、参照、ハンドリング方法の具体例

    // ファイル内容を読み込む  
    int nread = fread(data, sizeof(data[0]), DATA_LEN, fin);  

上記ではfread関数でファイル内容を読み込み、配列dataに保存しています。

#include <stdio.h>  

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);  

fread関数は第1引数にデータ保存先のバッファのポインタを取ります。
第2引数にはそのバッファの要素1つのサイズ(バイト数)を取ります。
第3引数にはバッファの要素数を取ります。
第4引数にはファイルポインタ(ストリーム)を指定します。
freadは読み込みに成功すると読み込んだ要素数を返します。

    data[nread] = '\0';  // ナル文字を付加  

上記ではdata[nread]の位置にナル文字を入れています。
fread関数が返した読み込んだ要素数をdataの添え字にして、そこにナル文字を入れています。
文字列の配列はお尻にナル文字が必要です。

    // データ内容を出力  
    printf("data[%s]\n", data);  

    // ファイルを閉じる  
    fclose(fin);  

上記ではdataを出力してfinを閉じています。
finを閉じ忘れるとメモリリークなどのバグになります。

動的に配列を作成しデータを読み込む

固定長の配列ではなく動的に配列を作成してデータを読み込む方法です。

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

int main(void) {  
    // ファイルを開く  
    FILE *fp = fopen("sample.txt", "r");  
    if (fp == NULL) {  
        perror("fopen");  
        return 1;  
    }  

    fseek(fp, 0L, SEEK_END);  // ファイル末尾にシーク  
    long pos = ftell(fp);  // ファイル末尾の位置を得る。これがファイルサイズになる  
    long size = pos + 1;  // + 1してナル文字分追加  
    fseek(fp, 0L, SEEK_SET);  // 位置をファイル先頭に戻す  

    char *s = malloc(size);  // 動的メモリを確保  
    if (s == NULL) {  
        perror("malloc");  
        fclose(fp);  
        return 1;  
    }  

    fread(s, sizeof(s[0]), pos, fp);  // ファイル内容を読み込み  
    s[pos] = '\0';  // ナル文字を付加  

    // ファイル内容の出力  
    printf("data[%s]\n", s);  

    // ファイルを閉じる  
    fclose(fp);    

    // メモリを解放  
    free(s);  

    return 0;  
}  

上記のコードを実行すると以下の結果になります。

data[Hello, World!  
Good World!  
Goodbye, World!  
]  
    // ファイルを開く  
    FILE *fp = fopen("sample.txt", "r");  
    if (fp == NULL) {  
        perror("fopen");  
        return 1;  
    }  

上記ではファイルを開いています。ここは固定長の時と同じです。

    fseek(fp, 0L, SEEK_END);  // ファイル末尾にシーク  
    long pos = ftell(fp);  // ファイル末尾の位置を得る。これがファイルサイズになる  
    long size = pos + 1;  // + 1してナル文字分追加  
    fseek(fp, 0L, SEEK_SET);  // 位置をファイル先頭に戻す  

上記ではファイルポインタfpからファイルサイズを取得しています。
まずfseek関数でfpの位置をファイル末尾にします。
それからftell関数でファイル内の位置を取得してposに保存します。
そいてposにナル文字分の1を足してsizeに保存します。
sizeが取得出来たらfpの位置を再びファイル先頭に戻します。

#include <stdio.h>  

int fseek(FILE *stream, long offset, int whence);  

上記のfseek関数は引数streamの内部位置を変更します。
whenceの位置からoffsetだけ移動させます。
whenceには以下の定数を指定できます。

  • SEEK_SET ... ファイル先頭
  • SEEK_CUR ... 現在位置
  • SEEK_END ... ファイル末尾
#include <stdio.h>  

long ftell(FILE *stream);  

上記のftell関数は引数streamの内部位置を取得します。

    char *s = malloc(size);  // 動的メモリを確保  
    if (s == NULL) {  
        perror("malloc");  
        fclose(fp);  
        return 1;  
    }  

上記では動的メモリを確保しています。
動的メモリとはヒープ領域から取ってくるメモリのことです。
メインメモリ上の大きなメモリを扱えます。
malloc関数でsizeのバイトだけメモリを確保してそれをsに入れます。
sがNULLだったらperror関数でエラーを出力してfclose関数でファイルポインタを閉じてからreturnします。

#include <stdlib.h>  

void *malloc(size_t size);  

上記のmalloc関数は引数のsizeバイトだけヒープ領域からメモリを確保します。
malloc関数はメモリの確保に失敗するとNULLを返し、成功した場合は確保したメモリのアドレスを返します。

    fread(s, sizeof(s[0]), pos, fp);  // ファイル内容を読み込み  
    s[pos] = '\0';  // ナル文字を付加  

上記ではfread関数を使ってファイル内容を読み込んでいます。
やってることは固定長の時と同じです。

    // ファイル内容の出力  
    printf("data[%s]\n", s);  

    // ファイルを閉じる  
    fclose(fp);    

    // メモリを解放  
    free(s);  

上記ではsを出力し、ファイルを閉じてsfree()しています。
smalloc関数で確保した動的メモリです。
動的メモリはfree関数で解放する必要があります。
解放を忘れるとメモリリークと言うバグになります。

#include <stdlib.h>  

void free(void *ptr);  

上記のfree関数は動的メモリで確保された引数ptrのメモリを解放します。

関連記事
自作関数read_fileでファイルを読み込み【ファイル入出力, コマンド】
C言語の静的なメモリと動的なメモリ