file_get_contents の C 言語版

ファイルの内容をメモリ上に取り込むのに、PHP には file_get_contents という便利な関数があって、

C 言語でも使いたいので、俺々 file_get_contents を実装してみる。

やりたい事

  • ファイル名を与えるとメモリに取り込んで先頭アドレスと長さを教えて欲しい。
  • ちなみに、C 言語の文字列は char * のポインタで扱う事になってて、そこに長さの情報は含まれない
    • C 言語を捨てて C++ 使えというのは却下で。(両方救おう。次 C++ 使う時にそっちも実装する)
  • 仕方ないので、ファイルデータの先頭ポインタ(char *)とデータ長(unsigned long)を struct にして一度で return する。
    • (実は C 言語は struct を return 出来るの。でも油断すると stack を浪費するので僕は悪手だと思ってる)

コード

  • file_get_contents.h
#ifndef __FILE_GET_CONTENTS_H__
#define __FILE_GET_CONTENTS_H__

struct file_text {
    char *data;
    unsigned long data_len;
};

extern struct file_text file_get_contents(char *filename);

#endif /* __FILE_GET_CONTENTS_H__ */
  • file_get_contents.c
#include <stdio.h>
#include <stdlib.h> // malloc/free
#include <sys/stat.h>
#include "file_get_contents.h"

static unsigned long file_get_contents_length(char *filename) {
    struct stat sbuf;
    if (stat(filename, &sbuf)) {
        fprintf(stderr, "Can't stat file(%s)\n", filename);
        return 0;
    }
    return sbuf.st_size;
}

struct file_text file_get_contents(char *filename) {
    struct file_text text;
    FILE *fp;
    text.data = NULL;
    text.data_len = file_get_contents_length(filename);
    if (text.data_len == 0) {
        return text;
    }
    fp = fopen(filename, "rb");
    if (fp == NULL) {
        fprintf(stderr, "Can't open infile(%s) for read\n", filename);
        return text;
    }
    text.data = malloc(text.data_len);
    if (fread(text.data, 1, text.data_len, fp) != text.data_len) {
        free(text.data);
        fclose(fp);
        return text;
    }
    fclose(fp);
    return text;
}
  • test.c
#include <stdio.h>
#include <stdlib.h> // free
#include "file_get_contents.h"

int main(int argc, char **argv) {
    struct file_text text;
    char *filename;
    int i;
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <anyfile>]\n", argv[0]);
        return EXIT_FAILURE;
    }
    filename = argv[1];
    text = file_get_contents(filename);
    if (text.data == NULL) {
        fprintf(stderr, "Not found or unreadable file(%s)\n", filename);
        return EXIT_FAILURE;
    }
    for (i = 0 ; i < text.data_len ; i++) {
        putchar(text.data[i] & 0xff);
    }
    free(text.data);
    return EXIT_SUCCESS;
}

テスト

yoya@sakura:~$ gcc test.c file_get_contents.c
yoya@sakura:~$ ./a.out file_get_contents.h
#ifndef __FILE_GET_CONTENTS_H__
#define __FILE_GET_CONTENTS_H__

struct file_text {
    char *data;
    unsigned long data_len;
};

extern struct file_text file_get_contents(char *filename);

#endif /* __FILE_GET_CONTENTS_H__ */
yoya@sakura:~$

最後に

カッコ良いのがあったら教えて!

(尚、mmap 版は必要になったら作る)