C言語の標準入出力とファイル入出力の基本

標準入出力 (Standard I/O)

標準入出力は、C言語で最も基本的なファイル操作です。`stdin`(標準入力)、`stdout`(標準出力)、`stderr`(標準エラー出力)の3つのストリームが事前に定義されており、これらはプログラムの起動時に自動的に開かれます。これらのストリームは、`FILE`型のポインタを通じて操作されます。

ファイルのオープンとクローズ

ファイルを操作するには、まず`fopen`関数を使用してファイルを開き、操作が終わったら`fclose`関数で閉じる必要があります。`fopen`は、ファイル名とモード(読み取り、書き込みなど)を指定してファイルポインタを返します。

#include <stdio.h>

int main() {
    // ファイルポインタの宣言
    FILE *file_handle = NULL;
    
    // 書き込みモードでファイルを開く
    file_handle = fopen("sample.txt", "w");
    if (file_handle == NULL) {
        perror("ファイルを開けませんでした");
        return -1;
    }
    printf("ファイルを開くことに成功しました
");

    // ファイルを閉じる
    fclose(file_handle);
    return 0;
}

文字単位の読み書き

`fputc`関数は、指定した文字をファイルに書き込み、`fgetc`関数はファイルから1文字を読み込みます。これらの関数は、ファイルの終わり(EOF)に達したかどうかを確認するために頻繁に使用されます。

#include <stdio.h>

int main() {
    FILE *file_handle = NULL;
    
    // 書き込みモードでファイルを開く
    file_handle = fopen("data.txt", "w");
    if (file_handle == NULL) {
        perror("ファイルを開けませんでした");
        return -1;
    }

    // ファイルにデータを書き込む
    fputc('H', file_handle);
    fputc('i', file_handle);

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

    // 読み取りモードでファイルを再オープン
    file_handle = fopen("data.txt", "r");
    if (file_handle == NULL) {
        perror("ファイルを開けませんでした");
        return -1;
    }
    
    char current_char;
    while (1) {
        current_char = fgetc(file_handle); // ファイルから1文字読み込む
        if (current_char == EOF) { // ファイルの終わりに達した場合
            break;
        }
        printf("%c ", current_char);
    }
    printf("
");
    
    // ファイルを閉じる
    fclose(file_handle);
    return 0;
}

文字列の読み書き

複数の文字を一度に扱うには、`fputs`と`fgets`を使用します。`fputs`は文字列をファイルに書き込み、`fgets`はファイルから文字列をバッファに読み込みます。

#include <stdio.h>

int main() {
    FILE *file_handle = NULL;
    
    // 書き込みモードでファイルを開く
    file_handle = fopen("message.txt", "w");
    if (file_handle == NULL) {
        perror("ファイルを開けませんでした");
        return -1;
    }

    // ファイルに文字列を書き込む
    fputs("こんにちは
", file_handle);
    fputs("世界
", file_handle);

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

    // 読み取りモードでファイルを再オープン
    file_handle = fopen("message.txt", "r");
    if (file_handle == NULL) {
        perror("ファイルを開けませんでした");
        return -1;
    }

    char buffer[10]; // 文字列を一時的に保持するバッファ
    while (1) {
        char *result = fgets(buffer, sizeof(buffer), file_handle); // ファイルから文字列を読み込む
        if (result == NULL) {
            break;
        }
        printf("バッファの内容: %s
", buffer);
    }
    
    // ファイルを閉じる
    fclose(file_handle);
    return 0;
}

バイナリデータの読み書き

テキストデータだけでなく、整数や構造体などのバイナリデータを効率的に読み書きするには、`fwrite`と`fread`を使用します。これらの関数は、データのサイズと個数を指定して操作を行います。

#include <stdio.h>

// 構造体の定義
struct Student {
    int id;
    char name[20];
    float grade;
};

int main() {
    FILE *file_handle = NULL;
    
    // 書き込みモードでファイルを開く
    file_handle = fopen("student.dat", "wb"); // バイナリ書き込みモード
    if (file_handle == NULL) {
        perror("ファイルを開けませんでした");
        return -1;
    }

    // 構造体の配列を定義
    struct Student students[2] = {{101, "田中", 85.5}, {102, "佐藤", 92.0}};

    // 構造体の配列をファイルに書き込む
    fwrite(students, sizeof(struct Student), 2, file_handle);

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

    // 読み取りモードでファイルを再オープン
    file_handle = fopen("student.dat", "rb"); // バイナリ読み取りモード
    if (file_handle == NULL) {
        perror("ファイルを開けませんでした");
        return -1;
    }

    struct Student read_student;
    // ファイルから1つの構造体を読み込む
    fread(&read_student, sizeof(struct Student), 1, file_handle);
    
    printf("ID: %d, 名前: %s, 成績: %.2f
", read_student.id, read_student.name, read_student.grade);
    
    // ファイルを閉じる
    fclose(file_handle);
    return 0;
}

ファイルポインタの操作

`fseek`関数を使用すると、ファイル内の任意の位置にポインタを移動させることができます。これにより、ランダムアクセスが可能になります。

#include <stdio.h>

int main() {
    FILE *file_handle = fopen("random_access.txt", "w+");
    if (file_handle == NULL) {
        perror("ファイルを開けませんでした");
        return -1;
    }

    // データを書き込む
    fputs("ABCDEFGHIJKLMNOPQRSTUVWXYZ", file_handle);

    // ファイルポインタを先頭に戻す
    fseek(file_handle, 0, SEEK_SET);

    // 10番目の文字から読み込む
    fseek(file_handle, 9, SEEK_SET);
    char ch;
    while ((ch = fgetc(file_handle)) != EOF) {
        putchar(ch);
    }
    printf("
");

    fclose(file_handle);
    return 0;
}

ファイル入出力 (File I/O)

ファイル入出力は、より低レベルの操作を提供します。システムコールを直接呼び出すことで、標準入出力よりも細かい制御が可能になります。主にファイル記述子(File Descriptor, fd)を介して操作が行われます。

ファイル記述子の基本操作

`open`関数はファイルを開き、ファイル記述子を返します。`read`と`write`は、ファイル記述子を指定してデータの読み書きを行います。`close`はファイル記述子を閉じます。

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int file_descriptor;
    
    // 書き込みモードでファイルを開く
    file_descriptor = open("low_level.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (file_descriptor == -1) {
        perror("ファイルを開けませんでした");
        return -1;
    }
    printf("ファイルを開くことに成功しました。fd=%d
", file_descriptor);

    // データを書き込む
    const char *data = "これは低レベルI/Oのテストです。";
    write(file_descriptor, data, sizeof(data) - 1); // NULL終端文字を除く

    // ファイルを閉じる
    close(file_descriptor);
    return 0;
}

ファイル記述子の複製

`dup`と`dup2`関数は、既存のファイル記述子を複製し、新しいファイル記述子を生成します。これにより、同じファイルに対して複数のストリームを扱うことができます。

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int original_fd, duplicated_fd;
    
    // ファイルを開く
    original_fd = open("duplicate_test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (original_fd == -1) {
        perror("ファイルを開けませんでした");
        return -1;
    }

    // データを書き込む
    const char *msg = "オリジナルのメッセージ";
    write(original_fd, msg, sizeof(msg) - 1);

    // ファイルポインタを先頭に戻す
    lseek(original_fd, 0, SEEK_SET);

    // ファイル記述子を複製する
    duplicated_fd = dup(original_fd);
    printf("元のfd: %d, 複製されたfd: %d
", original_fd, duplicated_fd);

    // 複製されたfdのポインタを移動させる
    lseek(duplicated_fd, 5, SEEK_SET);

    // 元のfdから読み込む
    char buffer[50];
    read(original_fd, buffer, sizeof(buffer) - 1);
    buffer[sizeof(buffer) - 1] = '\0'; // NULL終端
    printf("読み込んだデータ: %s
", buffer);

    close(original_fd);
    return 0;
}

ファイルの状態を取得する

`stat`システムコールは、ファイルの属性(サイズ、種類、権限など)を取得するために使用されます。

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "使用法: %s ファイル名
", argv[0]);
        return -1;
    }

    struct stat file_stat;
    if (stat(argv[1], &file_stat) == -1) {
        perror("statエラー");
        return -1;
    }

    // ファイルの種類を判別する
    switch (file_stat.st_mode & S_IFMT) {
        case S_IFREG: printf("通常ファイル
"); break;
        case S_IFDIR: printf("ディレクトリ
"); break;
        case S_IFLNK: printf("シンボリックリンク
"); break;
        // ... 他の種類
    }

    printf("モード: %#o\t サイズ: %ld bytes\t inode: %ld
", 
           file_stat.st_mode & 0777, file_stat.st_size, file_stat.st_ino);
    return 0;
}

タグ: C言語 標準入出力 ファイル入出力 fopen open

6月14日 16:07 投稿