[C言語] frwite関数を使ってファイルに書き込む方法

C言語でファイルにデータを書き込む際に使用するのがfwrite関数です。

この関数は、バイナリデータをファイルに書き込むために使用され、テキストデータの書き込みにも利用できます。

関数のシグネチャはsize_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream)で、ptrは書き込むデータのポインタ、sizeは各データ要素のサイズ、countは書き込む要素数、streamはファイルポインタを指定します。

成功すると書き込まれた要素数を返し、失敗すると0を返します。

この記事でわかること
  • fwrite関数の基本的な使い方とシンタックス
  • テキストファイルやバイナリファイルへの書き込み方法
  • 構造体や配列データのファイル書き込みの実例
  • fwrite関数を使用する際の注意点とエラー処理
  • fwrite関数とfprintf関数の違いと適切な使い分け

目次から探す

fwrite関数の基本

fwrite関数とは

fwrite関数は、C言語においてファイルにデータを書き込むための標準ライブラリ関数です。

この関数は、バイナリデータを効率的にファイルに書き込むことができ、特に大量のデータを扱う際に便利です。

fwrite関数のシンタックス

fwrite関数の基本的なシンタックスは以下の通りです。

size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);

fwrite関数の引数の説明

fwrite関数は4つの引数を取ります。

それぞれの引数の役割は以下の通りです。

スクロールできます
引数名説明
ptr書き込みたいデータの先頭アドレスを指すポインタ
size書き込むデータの1要素のサイズ(バイト単位)
count書き込む要素の数
stream書き込み先のファイルポインタ

fwrite関数の戻り値

fwrite関数は、実際に書き込まれた要素の数を返します。

もし、count個の要素がすべて正常に書き込まれた場合は、countが返されます。

書き込みに失敗した場合や、書き込まれた要素の数がcountより少ない場合は、エラーが発生した可能性があります。

fwrite関数のエラー処理

fwrite関数のエラー処理は、戻り値を確認することで行います。

戻り値がcountより少ない場合、エラーが発生した可能性があります。

エラーの詳細を知るためには、ferror関数を使用して、ファイルストリームのエラー状態を確認することができます。

if (fwrite(data, sizeof(data[0]), count, file) < count) {
    // エラーが発生した場合の処理
    if (ferror(file)) {
        perror("ファイル書き込みエラー");
    }
}

このように、fwrite関数を使用する際には、エラー処理を適切に行うことが重要です。

fwrite関数を使ったファイル書き込みの実例

テキストファイルへの書き込み

fwrite関数は主にバイナリデータの書き込みに使用されますが、テキストデータも書き込むことができます。

以下は、テキストファイルに文字列を書き込む例です。

#include <stdio.h>
#include <string.h>
int main() {
    FILE *file = fopen("example.txt", "w");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    const char *text = "こんにちは、世界!";
    fwrite(text, sizeof(char), strlen(text), file);
    fclose(file);
    return 0;
}

このプログラムは、”example.txt”というファイルに「こんにちは、世界!」というテキストを書き込みます。

バイナリファイルへの書き込み

バイナリデータの書き込みは、fwrite関数の得意とするところです。

以下は、整数の配列をバイナリファイルに書き込む例です。

#include <stdio.h>
int main() {
    FILE *file = fopen("data.bin", "wb");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    int numbers[] = {1, 2, 3, 4, 5};
    fwrite(numbers, sizeof(int), 5, file);
    fclose(file);
    return 0;
}

このプログラムは、”data.bin”というバイナリファイルに整数の配列をそのまま書き込みます。

構造体データの書き込み

構造体をファイルに書き込むことも可能です。

以下は、構造体をバイナリファイルに書き込む例です。

#include <stdio.h>
typedef struct {
    int id;
    char name[20];
} Person;
int main() {
    FILE *file = fopen("people.bin", "wb");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    Person person = {1, "山田太郎"};
    fwrite(&person, sizeof(Person), 1, file);
    fclose(file);
    return 0;
}

このプログラムは、”people.bin”というバイナリファイルにPerson構造体のデータを書き込みます。

配列データの書き込み

配列データをファイルに書き込むことも簡単です。

以下は、文字列の配列をファイルに書き込む例です。

#include <stdio.h>
int main() {
    FILE *file = fopen("strings.txt", "w");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    const char *strings[] = {"りんご", "みかん", "バナナ"};
    for (int i = 0; i < 3; i++) {
        fwrite(strings[i], sizeof(char), strlen(strings[i]), file);
        fwrite("\n", sizeof(char), 1, file); // 改行を追加
    }
    fclose(file);
    return 0;
}

このプログラムは、”strings.txt”というファイルに文字列の配列を1行ずつ書き込みます。

fwrite関数の応用例

大量データの効率的な書き込み

fwrite関数は、大量のデータを効率的にファイルに書き込む際に非常に有用です。

例えば、センサーデータやログデータなど、連続して生成されるデータをバッファにまとめて書き込むことで、ディスクI/Oの回数を減らし、パフォーマンスを向上させることができます。

#include <stdio.h>
#define BUFFER_SIZE 1024
int main() {
    FILE *file = fopen("large_data.bin", "wb");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    char buffer[BUFFER_SIZE];
    // バッファにデータを詰める処理(例として0で埋める)
    memset(buffer, 0, BUFFER_SIZE);
    for (int i = 0; i < 1000; i++) {
        fwrite(buffer, sizeof(char), BUFFER_SIZE, file);
    }
    fclose(file);
    return 0;
}

このプログラムは、”large_data.bin”というファイルに大量のデータを効率的に書き込みます。

ファイルへのログ出力

fwrite関数は、ログデータをファイルに出力する際にも利用できます。

ログをバイナリ形式で保存することで、データのサイズを削減し、読み書きの速度を向上させることができます。

#include <stdio.h>
#include <time.h>
int main() {
    FILE *file = fopen("log.bin", "ab");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    time_t now = time(NULL);
    fwrite(&now, sizeof(time_t), 1, file);
    fclose(file);
    return 0;
}

このプログラムは、現在の時刻を”log.bin”というファイルにバイナリ形式で追記します。

データベースのバックアップ

データベースのバックアップを取る際に、fwrite関数を使用してデータをファイルに書き出すことができます。

これにより、データの復元が必要な場合に備えることができます。

#include <stdio.h>
typedef struct {
    int id;
    char name[50];
} Record;
int main() {
    FILE *file = fopen("backup.bin", "wb");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    Record records[] = {
        {1, "Alice"},
        {2, "Bob"},
        {3, "Charlie"}
    };
    fwrite(records, sizeof(Record), 3, file);
    fclose(file);
    return 0;
}

このプログラムは、Record構造体の配列を”backup.bin”というファイルに書き込み、データベースのバックアップを作成します。

ネットワーク通信データの保存

ネットワーク通信で受信したデータをファイルに保存する際にも、fwrite関数は役立ちます。

受信したデータをそのままバイナリファイルに保存することで、後で解析や再送信が可能になります。

#include <stdio.h>
int main() {
    FILE *file = fopen("network_data.bin", "wb");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    char received_data[] = {0x01, 0x02, 0x03, 0x04}; // 受信データの例
    fwrite(received_data, sizeof(char), sizeof(received_data), file);
    fclose(file);
    return 0;
}

このプログラムは、受信したバイナリデータを”network_data.bin”というファイルに保存します。

fwrite関数を使う際の注意点

バッファリングの影響

fwrite関数を使用する際には、バッファリングの影響を考慮する必要があります。

標準ライブラリのI/O操作は通常、バッファリングされており、データはすぐにディスクに書き込まれるわけではありません。

バッファがいっぱいになるか、fflush関数が呼ばれるまで、データはメモリ上に保持されます。

これにより、プログラムが異常終了した場合、バッファ内のデータが失われる可能性があります。

#include <stdio.h>
int main() {
    FILE *file = fopen("buffered.txt", "w");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    fputs("バッファリングされたデータ", file);
    fflush(file); // バッファをフラッシュしてディスクに書き込む
    fclose(file);
    return 0;
}

エンディアンの考慮

異なるプラットフォーム間でバイナリデータをやり取りする場合、エンディアン(バイトオーダー)の違いに注意が必要です。

エンディアンが異なると、データの解釈が変わってしまうため、データを送受信する際には、エンディアンを統一するか、変換を行う必要があります。

#include <stdio.h>
#include <stdint.h>
uint32_t swap_endian(uint32_t value) {
    return ((value >> 24) & 0x000000FF) |
           ((value >> 8) & 0x0000FF00) |
           ((value << 8) & 0x00FF0000) |
           ((value << 24) & 0xFF000000);
}

ファイルサイズの制限

ファイルシステムやプラットフォームによっては、ファイルサイズに制限がある場合があります。

特に、32ビットシステムでは、2GBを超えるファイルを扱う際に注意が必要です。

fwrite関数を使用する際には、書き込むデータのサイズを考慮し、必要に応じてファイルを分割するなどの対策を講じることが重要です。

データの整合性

fwrite関数を使用してデータを書き込む際には、データの整合性を確保することが重要です。

特に、複数のプロセスやスレッドが同じファイルにアクセスする場合、データの競合が発生する可能性があります。

これを防ぐためには、ファイルロックを使用するか、書き込み操作を同期させる必要があります。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
    int fd = open("sync.txt", O_WRONLY | O_CREAT, 0644);
    if (fd == -1) {
        perror("ファイルオープンエラー");
        return 1;
    }
    // ファイルロックを取得
    if (flock(fd, LOCK_EX) == -1) {
        perror("ファイルロックエラー");
        close(fd);
        return 1;
    }
    // 書き込み操作
    write(fd, "同期されたデータ", 18);
    // ファイルロックを解除
    flock(fd, LOCK_UN);
    close(fd);
    return 0;
}

このように、fwrite関数を使用する際には、さまざまな注意点を考慮し、適切な対策を講じることが重要です。

よくある質問

fwrite関数で書き込みが失敗するのはなぜ?

fwrite関数で書き込みが失敗する原因はいくつか考えられます。

まず、ファイルが正しく開かれていない場合や、書き込み権限がない場合があります。

また、ディスクの空き容量が不足している場合や、ファイルシステムの制限に達している場合も考えられます。

エラーが発生した場合は、ferror関数を使用してエラーの詳細を確認し、適切なエラーハンドリングを行うことが重要です。

fwrite関数とfprintf関数の違いは?

fwrite関数fprintf関数は、どちらもファイルにデータを書き込むための関数ですが、用途が異なります。

fwrite関数はバイナリデータの書き込みに適しており、データをそのままの形式でファイルに書き込みます。

一方、fprintf関数はテキストデータの書き込みに適しており、フォーマット指定子を使用してデータを整形して書き込みます。

例:fprintf(file, "%d", number);

fwrite関数でEOFを確認する方法は?

fwrite関数自体は、EOF(End Of File)を確認するための機能を持っていません。

EOFは通常、ファイルの読み込み時に使用される概念です。

書き込み操作においては、fwrite関数の戻り値を確認することで、書き込みが正常に行われたかどうかを判断します。

書き込みが期待した要素数より少ない場合、エラーが発生した可能性があります。

まとめ

fwrite関数は、C言語でファイルにバイナリデータを書き込むための強力なツールです。

この記事では、fwrite関数の基本的な使い方から応用例、注意点、よくある質問までを詳しく解説しました。

これを機に、fwrite関数を活用して、効率的なファイル操作を実現してみてください。

当サイトはリンクフリーです。出典元を明記していただければ、ご自由に引用していただいて構いません。

関連カテゴリーから探す

  • 標準入出力 (47)
  • ファイル (76)
  • URLをコピーしました!
目次から探す