標準入出力

【C言語】fscanfの使い方:書式指定でファイルからデータを読み込む

fscanfはC言語でファイルからデータを読み込む関数で、指定した書式に従って入力を解析します。

使用方法はfscanf(ファイルポインタ, "書式指定子",変数…)の形式で、例えば整数を読み込む際は"%d"、文字列なら"%s"を使用します。

これにより、ファイル内のデータを適切な型の変数に格納することが可能です。

書式指定子を正しく設定することで、多様なデータ形式に対応できます。

fscanfの基本的な使用方法

fscanf関数は、C言語においてファイルから入力を読み取るために使用される標準ライブラリ関数です。

fscanfは、標準入力としてのscanfと似た形式を持ちますが、ファイルストリームからデータを読み込む点が異なります。

主に、ファイルから指定されたフォーマットに従ってデータを解析し、対応する変数に格納します。

fscanfのシンタックス

#include <stdio.h>
int fscanf(FILE *stream, const char *format, ...);
  • stream: データを読み込むファイルストリームへのポインタ。
  • format: 読み取るデータの形式を指定するフォーマット文字列。
  • ...: 読み取ったデータを格納するための変数のアドレス。

基本的な使用例

以下は、fscanfを使用してファイルから整数と文字列を読み取る基本的な例です。

#include <stdio.h>
int main() {
    FILE *file = fopen("data.txt", "r"); // "data.txt"を読み取りモードで開く
    if (file == NULL) {
        printf("ファイルを開くことができません。\n");
        return 1;
    }
    int number;
    char word[100];
    // ファイルから整数と文字列を読み取る
    fscanf(file, "%d %s", &number, word);
    printf("読み取った整数: %d\n", number);
    printf("読み取った文字列: %s\n", word);
    fclose(file); // ファイルを閉じる
    return 0;
}
読み取った整数: 42
読み取った文字列: HelloWorld

この例では、data.txtというファイルから整数値と文字列を読み取り、それぞれnumberwordに格納しています。

ファイルの内容が42 HelloWorldであった場合、上記の出力が得られます。

fscanfの戻り値

fscanfは、成功した読み取りの数を返します。

エラーが発生した場合や、ファイルの終端に達した場合はEOF(通常-1)が返されます。

これを利用して、ファイルの全データを読み取る際のループ制御などに活用できます。

#include <stdio.h>
int main() {
    FILE *file = fopen("data.txt", "r");
    if (file == NULL) {
        printf("ファイルを開くことができません。\n");
        return 1;
    }
    int number;
    char word[100];
    int ret;
    // fscanfが成功した回数を確認しながら読み取る
    while ((ret = fscanf(file, "%d %s", &number, word)) != EOF) {
        if (ret == 2) { // 正しく2つの項目が読み取れた場合
            printf("整数: %d, 文字列: %s\n", number, word);
        } else {
            printf("データの形式が正しくありません。\n");
        }
    }
    fclose(file);
    return 0;
}
整数: 42, 文字列: HelloWorld
整数: 100, 文字列: SampleText

この例では、data.txtに複数の整数と文字列が記載されている場合に、すべてのデータを逐次読み取って表示します。

書式指定子の種類と使い方

fscanfを効果的に使用するためには、書式指定子の理解が不可欠です。

書式指定子は、ファイルから読み取るデータの型や形式を指定するために使用されます。

以下に、主な書式指定子とその使い方を解説します。

主な書式指定子一覧

書式指定子説明
%d10進整数を読み取る
%f浮動小数点数を読み取る
%lf倍精度浮動小数点数を読み取る
%c単一の文字を読み取る
%s空白で区切られた文字列を読み取る
%x16進整数を読み取る
%o8進整数を読み取る
%u符号なし10進整数を読み取る
%ld符号付き長整数を読み取る
%llu符号なし長長整数を読み取る

書式指定子の使用例

整数の読み取り

#include <stdio.h>
int main() {
    FILE *file = fopen("numbers.txt", "r");
    if (file == NULL) {
        printf("ファイルを開けません。\n");
        return 1;
    }
    int a, b;
    fscanf(file, "%d %d", &a, &b);
    printf("a = %d, b = %d\n", a, b);
    fclose(file);
    return 0;
}
a = 10, b = 20

浮動小数点数の読み取り

#include <stdio.h>
int main() {
    FILE *file = fopen("float.txt", "r");
    if (file == NULL) {
        printf("ファイルを開けません。\n");
        return 1;
    }
    float pi;
    fscanf(file, "%f", &pi);
    printf("πの値 = %f\n", pi);
    fclose(file);
    return 0;
}
πの値 = 3.141593

文字列の読み取り

#include <stdio.h>
int main() {
    FILE *file = fopen("string.txt", "r");
    if (file == NULL) {
        printf("ファイルを開けません。\n");
        return 1;
    }
    char str[50];
    fscanf(file, "%s", str);
    printf("読み取った文字列: %s\n", str);
    fclose(file);
    return 0;
}
読み取った文字列: HelloWorld

複数のデータ型の組み合わせ

#include <stdio.h>
int main() {
    FILE *file = fopen("mixed.txt", "r");
    if (file == NULL) {
        printf("ファイルを開けません。\n");
        return 1;
    }
    int id;
    char name[50];
    float score;
    fscanf(file, "%d %s %f", &id, name, &score);
    printf("ID: %d, 名前: %s, スコア: %.2f\n", id, name, score);
    fclose(file);
    return 0;
}
ID: 101, 名前: Taro, スコア: 85.50

書式指定子の修飾子

書式指定子には、フィールド幅や精度などの修飾子を追加することで、より具体的な入力形式を指定することができます。

  • %5d: 5桁の幅で整数を読み取る。
  • %.2f: 少数点以下2桁までの浮動小数点数を読み取る。
  • %10s: 最大10文字の文字列を読み取る。

応用的な書式指定子の使用

ファイルのデータが特定の形式になっている場合、より複雑な書式指定子を使用することも可能です。

例えば、カンマ区切りのデータを読み取る場合などです。

#include <stdio.h>
int main() {
    FILE *file = fopen("csv.txt", "r");
    if (file == NULL) {
        printf("ファイルを開けません。\n");
        return 1;
    }
    char name[50];
    int age;
    float salary;
    fscanf(file, "%[^,],%d,%f", name, &age, &salary);
    printf("名前: %s, 年齢: %d, 給与: %.2f\n", name, age, salary);
    fclose(file);
    return 0;
}
名前: Alice, 年齢: 30, 給与: 55000.00

この例では、%[^,]を使用してカンマ以外の文字を読み取ることで、カンマ区切りの名前を正確に取得しています。

fscanfを活用した具体的なプログラム例

ここでは、fscanfを用いた具体的なプログラム例をいくつか紹介します。

これらの例を通じて、fscanfの実践的な使い方を理解しましょう。

例1: 学生の情報をファイルから読み取る

students.txtファイルに、学生のID、名前、成績が記載されているとします。

students.txt

101 Taro 85.5
102 Hanako 92.0
103 Jiro 78.3

プログラムコード

#include <stdio.h>
int main() {
    FILE *file = fopen("students.txt", "r");
    if (file == NULL) {
        printf("ファイルを開けません。\n");
        return 1;
    }
    int id;
    char name[50];
    float score;
    printf("学生の情報:\n");
    while (fscanf(file, "%d %s %f", &id, name, &score) != EOF) {
        printf("ID: %d, 名前: %s, スコア: %.1f\n", id, name, score);
    }
    fclose(file);
    return 0;
}
学生の情報:
ID: 101, 名前: Taro, スコア: 85.5
ID: 102, 名前: Hanako, スコア: 92.0
ID: 103, 名前: Jiro, スコア: 78.3

例2: 複数のデータ型を組み合わせた読み取り

products.txtファイルに、商品ID(整数)、商品名(文字列)、価格(浮動小数点数)、在庫数(整数)が記載されているとします。

products.txt

201 Apple 1.20 100
202 Banana 0.80 150
203 Cherry 2.50 50

プログラムコード

#include <stdio.h>
int main() {
    FILE *file = fopen("products.txt", "r");
    if (file == NULL) {
        printf("ファイルを開けません。\n");
        return 1;
    }
    int productId;
    char productName[50];
    float price;
    int stock;
    printf("商品リスト:\n");
    while (fscanf(file, "%d %s %f %d", &productId, productName, &price, &stock) != EOF) {
        printf("商品ID: %d, 名前: %s, 価格: %.2f, 在庫: %d\n", productId, productName, price, stock);
    }
    fclose(file);
    return 0;
}
商品リスト:
商品ID: 201, 名前: Apple, 価格: 1.20, 在庫: 100
商品ID: 202, 名前: Banana, 価格: 0.80, 在庫: 150
商品ID: 203, 名前: Cherry, 価格: 2.50, 在庫: 50

例3: カンマ区切りのデータを読み取る

data.csvファイルに、名前、年齢、給与がカンマ区切りで記載されているとします。

data.csv

Alice,30,55000.00
Bob,25,48000.50
Charlie,28,50000.00

プログラムコード

#include <stdio.h>
int main() {
    FILE *file = fopen("data.csv", "r");
    if (file == NULL) {
        printf("ファイルを開けません。\n");
        return 1;
    }
    char name[50];
    int age;
    float salary;
    printf("従業員データ:\n");
    while (fscanf(file, "%[^,],%d,%f", name, &age, &salary) != EOF) {
        printf("名前: %s, 年齢: %d, 給与: %.2f\n", name, age, salary);
    }
    fclose(file);
    return 0;
}
従業員データ:
名前: Alice, 年齢: 30, 給与: 55000.00
名前: Bob, 年齢: 25, 給与: 48000.50
名前: Charlie, 年齢: 28, 給与: 50000.00

例4: 複数行にわたるデータの読み取り

multiline.txtファイルに、複数行にわたってデータが記載されている場合の読み取り例です。

multiline.txt

1 John 75.0
2 Jane 88.5
3 Mike 64.2
4 Emily 91.3

プログラムコード

#include <stdio.h>
int main() {
    FILE *file = fopen("multiline.txt", "r");
    if (file == NULL) {
        printf("ファイルを開けません。\n");
        return 1;
    }
    int id;
    char name[50];
    float score;
    printf("複数行のデータ:\n");
    while (fscanf(file, "%d %s %f", &id, name, &score) != EOF) {
        printf("行ID: %d, 名前: %s, スコア: %.1f\n", id, name, score);
    }
    fclose(file);
    return 0;
}
複数行のデータ:
行ID: 1, 名前: John, スコア: 75.0
行ID: 2, 名前: Jane, スコア: 88.5
行ID: 3, 名前: Mike, スコア: 64.2
行ID: 4, 名前: Emily, スコア: 91.3

例5: 構造体を用いたデータの読み取り

データを構造体に格納して管理する方法です。

employees.txt

001 Alice 50000
002 Bob 45000
003 Charlie 47000

プログラムコード

#include <stdio.h>
typedef struct {
    char id[10];
    char name[50];
    int salary;
} Employee;
int main() {
    FILE *file = fopen("employees.txt", "r");
    if (file == NULL) {
        printf("ファイルを開けません。\n");
        return 1;
    }
    Employee emp;
    printf("従業員情報:\n");
    while (fscanf(file, "%s %s %d", emp.id, emp.name, &emp.salary) != EOF) {
        printf("ID: %s, 名前: %s, 給与: %d\n", emp.id, emp.name, emp.salary);
    }
    fclose(file);
    return 0;
}
従業員情報:
ID: 001, 名前: Alice, 給与: 50000
ID: 002, 名前: Bob, 給与: 45000
ID: 003, 名前: Charlie, 給与: 47000

これらの例を通じて、fscanfがさまざまなデータ形式や構造に対応できる柔軟な関数であることが理解できるでしょう。

実際のアプリケーションでは、ファイルの内容や目的に応じて適切な書式指定子と読み取りロジックを選択することが重要です。

よくあるエラーとその対処法

fscanfを使用する際には、いくつかのエラーが発生する可能性があります。

以下では、よく遭遇するエラーとその対処法について解説します。

エラー1: ファイルが開けない

概要: 指定したファイルが存在しない、またはアクセス権限がない場合に発生します。

対処法:

  • ファイル名やパスを確認する。
  • ファイルが存在することを確認する。
  • プログラムに適切なアクセス権限があるか確認する。

コード例

#include <stdio.h>
int main() {
    FILE *file = fopen("nonexistent.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    // その他の処理
    fclose(file);
    return 0;
}

エラー2: 書式指定子の不一致

概要: 読み取るデータの型と書式指定子が一致しない場合、期待した値が取得できません。

: 整数を読み取るために%dを使用すべきところ、%fを使用してしまう。

対処法:

  • データの型に適した書式指定子を使用する。
  • ファイル内のデータ形式を確認する。

コード例

誤った書式指定子の使用:

#include <stdio.h>
int main() {
    FILE *file = fopen("numbers.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    float number;
    fscanf(file, "%f", &number); // 本来は整数なので%fは誤り
    printf("読み取った数値: %f\n", number);
    fclose(file);
    return 0;
}

修正後

#include <stdio.h>
int main() {
    FILE *file = fopen("numbers.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    int number;
    fscanf(file, "%d", &number); // 正しい書式指定子
    printf("読み取った数値: %d\n", number);
    fclose(file);
    return 0;
}

エラー3: バッファオーバーフロー

概要: %sを使用する際に、読み取る文字列がバッファのサイズを超えると、バッファオーバーフローが発生します。

対処法:

  • %sを使用する場合、最大文字数を制限する書式指定子を使用する(例: %49s)。
  • 読み取るデータの長さをファイル側で制限する。

コード例

バッファオーバーフローの危険がある例:

#include <stdio.h>
int main() {
    FILE *file = fopen("longstring.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    char str[10];
    fscanf(file, "%s", str); // 長い文字列の場合、オーバーフローの危険
    printf("読み取った文字列: %s\n", str);
    fclose(file);
    return 0;
}

修正後

#include <stdio.h>
int main() {
    FILE *file = fopen("longstring.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    char str[10];
    fscanf(file, "%9s", str); // 最大9文字まで読み取る
    printf("読み取った文字列: %s\n", str);
    fclose(file);
    return 0;
}

エラー4: 不完全なデータの読み取り

概要: ファイルに期待した形式のデータが不足している場合、fscanfは正しくデータを読み取れず、部分的なデータしか取得できません。

対処法:

  • fscanfの戻り値をチェックし、期待した数の項目が読み取れたか確認する。
  • ファイルのデータ形式を正しく保つ。

コード例

不完全なデータを扱う例:

#include <stdio.h>
int main() {
    FILE *file = fopen("incomplete.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    int id;
    char name[50];
    float score;
    // fscanfの戻り値をチェック
    if (fscanf(file, "%d %s %f", &id, name, &score) != 3) {
        printf("エラー: データが不足しています。\n");
    } else {
        printf("ID: %d, 名前: %s, スコア: %.1f\n", id, name, score);
    }
    fclose(file);
    return 0;
}

エラー5: ファイルの終端まで読み取りすぎる

概要: ファイルの終端まで読み取った後に更に読み取りを試みると、エラーが発生します。

対処法:

  • ループ内でfscanfの戻り値を確認し、EOFに達したらループを終了する。
  • 読み取り前にファイルの状態を確認する。

コード例

正しいファイルの終端チェック:

#include <stdio.h>
int main() {
    FILE *file = fopen("data.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    int number;
    while (fscanf(file, "%d", &number) != EOF) {
        printf("読み取った数値: %d\n", number);
    }
    fclose(file);
    return 0;
}

エラー6: 書式指定子の誤りによるデータの誤解釈

概要: 書式指定子がデータの実際の形式と一致していない場合、データを誤って解釈してしまうことがあります。

対処法:

  • データの正確な形式を確認し、それに応じた書式指定子を使用する。
  • 必要に応じて、複雑なデータ形式に対応するために複数のfscanfを組み合わせる。

コード例

誤った書式指定子の使用:

#include <stdio.h>
int main() {
    FILE *file = fopen("data_mismatch.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    float id;
    char name[50];
    fscanf(file, "%f %s", &id, name); // IDは整数だが%fを使用
    printf("ID: %.2f, 名前: %s\n", id, name);
    fclose(file);
    return 0;
}

修正後

#include <stdio.h>
int main() {
    FILE *file = fopen("data_mismatch.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    int id;
    char name[50];
    fscanf(file, "%d %s", &id, name); // 正しい書式指定子
    printf("ID: %d, 名前: %s\n", id, name);
    fclose(file);
    return 0;
}

エラー7: 空白や特殊文字の扱い

概要: %sは空白を含まない文字列を読み取りますので、空白を含む文字列を正しく読み取ることができません。

対処法:

  • %sの代わりに、%[^\n]%[^\r\n]などを使用して空白を含む文字列を読み取る。
  • 必要に応じて、fgetssscanfを組み合わせて使用する。

コード例

空白を含む文字列を読む場合の工夫:

#include <stdio.h>
int main() {
    FILE *file = fopen("sentence.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    char sentence[200];
    fscanf(file, "%[^\n]", sentence); // 改行までの文字列を読み取る
    printf("読み取った文: %s\n", sentence);
    fclose(file);
    return 0;
}
読み取った文: This is a sample sentence with spaces.

エラー8: 不正なファイル形式

概要: ファイルの内容が期待する形式と異なる場合、データの読み取りに失敗します。

対処法:

  • ファイルの内容を事前に確認し、期待する形式に一致させる。
  • プログラム内でデータの検証を行い、不正なデータに対処する。

コード例

ファイル形式が不正な場合の処理:

#include <stdio.h>
int main() {
    FILE *file = fopen("invalid_format.txt", "r");
    if (file == NULL) {
        printf("エラー: ファイルが開けません。\n");
        return 1;
    }
    int id;
    char name[50];
    float score;
    if (fscanf(file, "%d %s %f", &id, name, &score) != 3) {
        printf("エラー: ファイルの形式が不正です。\n");
    } else {
        printf("ID: %d, 名前: %s, スコア: %.1f\n", id, name, score);
    }
    fclose(file);
    return 0;
}

fscanfは強力な関数ですが、使用する際にはデータの形式や書式指定子の適切な選択、エラーチェックなどに注意が必要です。

上記のようなよくあるエラーとその対処法を理解し、正しくfscanfを活用することで、安全かつ効率的にファイルからデータを読み取ることが可能になります。

まとめ

この記事では、C言語におけるfscanf関数の使用方法について詳細に説明しました。

さまざまな書式指定子と具体的なプログラム例を通じて、ファイルからのデータ読み取りの方法を理解できたでしょう。

実際の開発でfscanfを活用し、効率的なデータ処理を実践してみてください。

関連記事

Back to top button
目次へ