標準入出力

【C言語】fgetsの使い方:安全な文字列読み込みとバッファ管理

C言語のfgets関数は、指定したバッファサイズまで標準入力やファイルから安全に文字列を読み込むために使用されます。

バッファオーバーフローを防ぎ、改行文字を含む場合でも適切に処理します。

使用時はバッファサイズを正確に指定し、戻り値をチェックして入力の成功を確認することが重要です。

また、読み込まれた文字列は自動的にヌル終端されるため、安全な文字列操作が可能です。

fgetsの基本と特徴

fgetsは、C言語において安全に文字列を入力するための標準ライブラリ関数です。

fgetsを使用することで、バッファオーバーフローのリスクを軽減し、ユーザーからの入力を適切に管理することができます。

以下では、fgetsの基本的な使用方法とその特徴について詳しく解説します。

fgetsの基本的な使い方

fgets関数は、指定されたファイルストリームから文字列を読み込みます。

一般的に、標準入力stdinからユーザーの入力を受け取る際に使用されます。

fgetsの関数プロトタイプは以下の通りです:

char *fgets(char *str, int n, FILE *stream);
  • str: 読み込んだ文字列を格納するバッファへのポインタ。
  • n: 読み込む最大文字数。最後の文字はヌル文字 \0 になります。
  • stream: 読み込み元のファイルストリーム。通常は stdin を指定します。

なぜfgetsを使うのか

従来、文字列入力には gets関数が使用されていましたが、getsはバッファのサイズを考慮せずに入力を受け取るため、バッファオーバーフローのリスクが高く推奨されていません。

一方、fgetsは読み込む文字数を指定できるため、安全に文字列を扱うことが可能です。

改行文字の取り扱い

fgetsは、入力された改行文字 \n もバッファに含めて読み込みます。

そのため、取得した文字列の末尾に改行文字が含まれることを考慮する必要があります。

必要に応じて、改行文字を削除する処理を追加することが一般的です。

以下に、fgetsを使用してユーザーからの入力を安全に読み込み、表示するサンプルプログラムを示します。

#include <stdio.h>
#include <string.h>
int main() {
    char buffer[100]; // 入力を格納するバッファ
    printf("文字列を入力してください: ");
    if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
        // 改行文字を削除
        size_t len = strlen(buffer);
        if (len > 0 && buffer[len - 1] == '\n') {
            buffer[len - 1] = '\0';
        }
        printf("入力された文字列: %s\n", buffer);
    } else {
        printf("入力エラーが発生しました。\n");
    }
    return 0;
}
文字列を入力してください: Hello, World!
入力された文字列: Hello, World!

ポイントまとめ

  • 安全性: fgetsは読み込む文字数を指定できるため、バッファオーバーフローを防ぐことができます。
  • 改行文字の扱い: 入力された改行文字もバッファに含まれるため、必要に応じて削除する処理が必要です。
  • エラーハンドリング: fgetsNULLを返す場合は、入力エラーが発生したことを示します。適切なエラーハンドリングを行うことが推奨されます。

fgetsを適切に使用することで、安全かつ効率的に文字列を入力・管理することが可能です。

次のセクションでは、バッファサイズの設定方法について詳しく解説します。

バッファサイズの設定方法

fgetsを安全に使用するためには、適切なバッファサイズの設定が非常に重要です。

バッファサイズの設定が不適切だと、入力データがバッファに収まりきらず、データの欠損や予期せぬ動作を引き起こす可能性があります。

ここでは、バッファサイズの選び方とその管理方法について詳しく解説します。

バッファサイズの決定基準

バッファサイズを設定する際には、以下のポイントを考慮する必要があります。

  1. 予想される入力の最大長:
  • ユーザーが入力する可能性のある文字列の最大長を予測します。例えば、ユーザー名やパスワードなど、入力の種類に応じて適切な長さを設定します。
  1. 余裕を持ったサイズ設定:
  • 予想される最大長に加えて、ヌル文字 \0 や改行文字 \n を格納するための余裕を持たせます。
  1. メモリの効率的な使用:
  • 必要以上に大きなバッファを設定すると、メモリの無駄遣いになります。逆に小さすぎると入力が切れてしまうため、バランスが重要です。

固定バッファ vs 動的バッファ

バッファサイズの設定方法には、主に固定バッファと動的バッファの2種類があります。

固定バッファ

固定バッファでは、プログラムのコンパイル時にバッファサイズを決定します。

シンプルで実装が容易ですが、柔軟性に欠ける場合があります。

例: ユーザー名の入力バッファ

#include <stdio.h>
#include <string.h>
int main() {
    char username[50]; // ユーザー名用の固定バッファ
    printf("ユーザー名を入力してください: ");
    if (fgets(username, sizeof(username), stdin) != NULL) {
        // 改行文字を削除
        size_t len = strlen(username);
        if (len > 0 && username[len - 1] == '\n') {
            username[len - 1] = '\0';
        }
        printf("こんにちは、%sさん!\n", username);
    } else {
        printf("入力エラーが発生しました。\n");
    }
    return 0;
}
ユーザー名を入力してください: 山田太郎
こんにちは、山田太郎さん!

動的バッファ

動的バッファでは、実行時に必要なメモリを動的に割り当てます。

入力の長さが事前に不明な場合や、メモリ効率を重視する場合に有効です。

ただし、メモリ管理が複雑になるため、適切なエラーチェックが必要です。

例: 動的にバッファを割り当てる

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
    int bufferSize = 100; // 初期バッファサイズ
    char *buffer = malloc(bufferSize);
    if (buffer == NULL) {
        fprintf(stderr, "メモリの割り当てに失敗しました。\n");
        return 1;
    }
    printf("文字列を入力してください: ");
    if (fgets(buffer, bufferSize, stdin) != NULL) {
        // 改行文字を削除
        size_t len = strlen(buffer);
        if (len > 0 && buffer[len - 1] == '\n') {
            buffer[len - 1] = '\0';
        }
        printf("入力された文字列: %s\n", buffer);
    } else {
        printf("入力エラーが発生しました。\n");
    }
    free(buffer); // メモリの解放
    return 0;
}
文字列を入力してください: Hello, Dynamic Buffer!
入力された文字列: Hello, Dynamic Buffer!

バッファサイズ不足時の対処法

バッファサイズが不足すると、入力が切れてしまい、データの一部が失われる可能性があります。

これを防ぐための方法をいくつか紹介します。

  1. 入力の再試行を促す:
  • 入力が切れた場合にユーザーに再入力を促す方法です。
  1. バッファサイズを動的に拡張する:
  • 必要に応じてバッファサイズを拡張し、再度入力を受け付けます。
  1. 入力の部分的な処理:
  • バッファに収まる部分だけを処理し、残りを無視する方法です。ただし、これはデータの欠損につながるため、慎重に使用する必要があります。

例: バッファサイズをチェックし、再入力を促す

#include <stdio.h>
#include <string.h>
#define BUFFER_SIZE 10 // 小さなバッファサイズ
int main() {
    char buffer[BUFFER_SIZE];
    printf("短い文字列を入力してください(最大 %d 文字): ", BUFFER_SIZE - 1);
    if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
        // 改行文字が含まれているかチェック
        size_t len = strlen(buffer);
        if (len > 0 && buffer[len - 1] != '\n') {
            // 入力が切れている場合
            printf("入力が長すぎます。再度入力してください。\n");
            // 入力の残りをクリア
            int c;
            while ((c = getchar()) != '\n' && c != EOF);
        } else {
            // 改行文字を削除
            if (len > 0 && buffer[len - 1] == '\n') {
                buffer[len - 1] = '\0';
            }
            printf("入力された文字列: %s\n", buffer);
        }
    } else {
        printf("入力エラーが発生しました。\n");
    }
    return 0;
}
短い文字列を入力してください(最大 9 文字): この文字列は長すぎます。
入力が長すぎます。再度入力してください。

適切なバッファサイズ選択のベストプラクティス

  • 用途に応じたサイズ設定:
    • 例えば、メールアドレスの入力には100文字程度、コメント欄には500文字程度など、用途に応じて適切なサイズを選びます。
  • セキュリティを考慮:
    • バッファサイズを大きく設定しすぎると、メモリの無駄遣いになるだけでなく、攻撃者によるリソースの消費を招く可能性があります。必要最低限のサイズを設定することが推奨されます。
  • コードの保守性:
    • マジックナンバー(固定値)を避け、定数やマクロを使用してバッファサイズを定義することで、後からサイズを変更しやすくします。

例: マクロを使用してバッファサイズを定義

#include <stdio.h>
#include <string.h>
#define INPUT_SIZE 256 // 入力サイズをマクロで定義
int main() {
    char input[INPUT_SIZE];
    printf("コメントを入力してください(最大 %d 文字): ", INPUT_SIZE - 1);
    if (fgets(input, sizeof(input), stdin) != NULL) {
        // 改行文字を削除
        size_t len = strlen(input);
        if (len > 0 && input[len - 1] == '\n') {
            input[len - 1] = '\0';
        }
        printf("入力されたコメント: %s\n", input);
    } else {
        printf("入力エラーが発生しました。\n");
    }
    return 0;
}
コメントを入力してください(最大 255 文字): これはテストコメントです。
入力されたコメント: これはテストコメントです。

適切なバッファサイズの設定は、fgetsを安全かつ効果的に使用するための基本です。

次のセクションでは、エラーハンドリングと安全な使用法について詳しく解説します。

エラーハンドリングと安全な使用法

fgetsを安全に使用するためには、エラーハンドリングを適切に行うことが不可欠です。

エラーハンドリングを怠ると、予期せぬ動作やセキュリティ上の脆弱性を招く可能性があります。

本セクションでは、fgets使用時のエラーハンドリング手法と、安全に関数を活用するためのベストプラクティスについて詳しく解説します。

fgetsの戻り値の確認

fgets関数は、正常に文字列を読み込んだ場合には読み込んだ文字列へのポインタを返し、エラーが発生した場合やファイルの終端に達した場合にはNULLを返します。

したがって、fgetsを使用する際には、その戻り値を必ずチェックする必要があります。

#include <stdio.h>
#include <string.h>
int main() {
    char buffer[100];
    printf("文字列を入力してください: ");
    if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
        // 正常に入力が読み込まれた場合の処理
        size_t len = strlen(buffer);
        if (len > 0 && buffer[len - 1] == '\n') {
            buffer[len - 1] = '\0'; // 改行文字の削除
        }
        printf("入力された文字列: %s\n", buffer);
    } else {
        // エラーまたはEOFが発生した場合の処理
        fprintf(stderr, "入力エラーが発生しました。\n");
    }
    return 0;
}
文字列を入力してください: テスト入力
入力された文字列: テスト入力

エラーの原因を特定する

fgetsNULLを返す場合、エラーの原因を特定するためにerrnoを参照することが推奨されます。

errnoには、直前に発生したエラーの番号が格納されており、perror関数やstrerror関数を使用してエラーメッセージを取得できます。

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
    char buffer[100];
    printf("文字列を入力してください: ");
    if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
        size_t len = strlen(buffer);
        if (len > 0 && buffer[len - 1] == '\n') {
            buffer[len - 1] = '\0';
        }
        printf("入力された文字列: %s\n", buffer);
    } else {
        // エラーの原因を出力
        perror("fgetsエラー");
    }
    return 0;
}
文字列を入力してください:
fgetsエラー: ファイルの終端に達しました

入力バッファのクリア

fgetsを使用しても、ユーザーが指定したバッファサイズを超える入力を行った場合、入力バッファに残りのデータが残ることがあります。

これにより、次回の入力時に予期せぬ動作が発生する可能性があります。

入力バッファをクリアすることで、この問題を回避できます。

#include <stdio.h>
#include <string.h>
#define BUFFER_SIZE 10
int main() {
    char buffer[BUFFER_SIZE];
    printf("短い文字列を入力してください(最大 %d 文字): ", BUFFER_SIZE - 1);
    if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
        size_t len = strlen(buffer);
        if (len > 0 && buffer[len - 1] != '\n') {
            // 入力がバッファを超えている場合、残りの入力をクリア
            int ch;
            while ((ch = getchar()) != '\n' && ch != EOF);
            fprintf(stderr, "入力が長すぎます。入力が一部切り捨てられました。\n");
        } else if (len > 0 && buffer[len - 1] == '\n') {
            buffer[len - 1] = '\0';
        }
        printf("入力された文字列: %s\n", buffer);
    } else {
        fprintf(stderr, "入力エラーが発生しました。\n");
    }
    return 0;
}
短い文字列を入力してください(最大 9 文字): これはとても長い入力です。
入力が長すぎます。入力が一部切り捨てられました。
入力された文字列: これはとても

安全な使用法のベストプラクティス

fgetsを安全に使用するためのベストプラクティスを以下にまとめます。

  1. バッファサイズを適切に設定する:
  • 読み込む文字数を正確に把握し、必要なバッファサイズを確保します。前セクション「バッファサイズの設定方法」を参照してください。
  1. 戻り値を常にチェックする:
  • fgetsの戻り値がNULLでないことを確認し、必要に応じてエラーハンドリングを行います。
  1. 改行文字を適切に処理する:
  • fgetsは改行文字も読み込むため、必要に応じて削除する処理を実装します。
  1. 入力バッファをクリアする:
  • バッファサイズを超える入力があった場合、残りの入力をクリアして次回の入力に影響を与えないようにします。
  1. エラーメッセージを適切に出力する:
  • perrorstrerrorを使用して、ユーザーや開発者に対して有用なエラーメッセージを提供します。
  1. 定数やマクロを使用してバッファサイズを管理する:
  • マジックナンバーを避け、コードの可読性と保守性を向上させます。
  1. 必要に応じて動的バッファを使用する:
  • 入力サイズが事前に不明な場合や、メモリ効率を重視する場合には、動的バッファの使用を検討します。

例: エラーハンドリングを含む安全なfgetsの使用

以下に、エラーハンドリングを適切に実装したfgetsの使用例を示します。

このプログラムは、ユーザーからの入力を安全に読み込み、エラー発生時には適切なメッセージを表示します。

#include <stdio.h>
#include <string.h>
#include <errno.h>
#define INPUT_SIZE 50
int main() {
    char input[INPUT_SIZE];
    printf("コメントを入力してください(最大 %d 文字): ", INPUT_SIZE - 1);
    if (fgets(input, sizeof(input), stdin) != NULL) {
        size_t len = strlen(input);
        if (len > 0 && input[len - 1] == '\n') {
            input[len - 1] = '\0'; // 改行文字の削除
        } else if (len > 0 && input[len - 1] != '\n') {
            // 入力がバッファを超えている場合、残りの入力をクリア
            int ch;
            while ((ch = getchar()) != '\n' && ch != EOF);
            fprintf(stderr, "入力が長すぎます。入力が一部切り捨てられました。\n");
        }
        printf("入力されたコメント: %s\n", input);
    } else {
        // エラー発生時のメッセージ出力
        perror("fgetsエラー");
    }
    return 0;
}
コメントを入力してください(最大 49 文字): これはテストコメントです。
入力されたコメント: これはテストコメントです。
コメントを入力してください(最大 49 文字): これは非常に長いテストコメントであり、バッファサイズを超えています。
入力が長すぎます。入力が一部切り捨てられました。
入力されたコメント: これは非常に長いテストコメントであり、バッファ

fgetsを安全に使用するためには、エラーハンドリングを適切に実装し、バッファサイズや入力内容に応じた処理を行うことが重要です。

以下のポイントを押さえて、堅牢なプログラムを作成しましょう。

  • 戻り値のチェック: fgetsの戻り値を必ず確認し、NULLの場合の対処を行う。
  • 入力バッファの管理: バッファサイズを超える入力に対して適切に対応し、入力バッファをクリアする。
  • エラーメッセージの提供: ユーザーや開発者に有用なエラーメッセージを出力する。
  • バッファサイズの適切な設定: 必要なバッファサイズを確保し、メモリの無駄遣いやデータの欠損を防ぐ。

これらのベストプラクティスを守ることで、fgetsを安全かつ効果的に活用し、信頼性の高いCプログラムを開発することができます。

実践的なfgetsの活用例

fgetsは、ユーザーからの入力やファイルからのデータ読み込みなど、さまざまな場面で安全な文字列入力を実現するために活用されます。

ここでは、fgetsを用いた実践的な活用例をいくつか紹介し、それぞれの具体的な実装方法と動作を解説します。

ユーザーからの入力を安全に取得する

ユーザーからの入力を安全に取得し、入力内容を処理する基本的な例です。

前セクションで紹介した内容を踏まえ、ユーザー名を入力して挨拶するプログラムを作成します。

#include <stdio.h>
#include <string.h>
#define USERNAME_SIZE 50
int main() {
    char username[USERNAME_SIZE];
    printf("ユーザー名を入力してください: ");
    if (fgets(username, sizeof(username), stdin) != NULL) {
        // 改行文字を削除
        size_t len = strlen(username);
        if (len > 0 && username[len - 1] == '\n') {
            username[len - 1] = '\0';
        }
        printf("こんにちは、%sさん!\n", username);
    } else {
        fprintf(stderr, "入力エラーが発生しました。\n");
    }
    return 0;
}
ユーザー名を入力してください: 山田太郎
こんにちは、山田太郎さん!

ファイルからの文字列読み込み

fgetsを使用して、テキストファイルから1行ずつ文字列を読み込む例です。

ファイルからのデータ読み込みは、設定ファイルやログファイルの解析などに有用です。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINE_SIZE 256
int main() {
    FILE *file = fopen("sample.txt", "r");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    char line[LINE_SIZE];
    while (fgets(line, sizeof(line), file) != NULL) {
        // 改行文字を削除
        size_t len = strlen(line);
        if (len > 0 && line[len - 1] == '\n') {
            line[len - 1] = '\0';
        }
        printf("読み込んだ行: %s\n", line);
    }
    if (ferror(file)) {
        perror("ファイル読み込みエラー");
    }
    fclose(file);
    return 0;
}
これはサンプルの1行目です。
これはサンプルの2行目です。
最後の行です。
読み込んだ行: これはサンプルの1行目です。
読み込んだ行: これはサンプルの2行目です。
読み込んだ行: 最後の行です。

入力の検証と再入力の促進

ユーザーが入力したデータを検証し、必要に応じて再入力を促すプログラムです。

例えば、パスワードの長さをチェックし、基準を満たさない場合に再入力を要求します。

#include <stdio.h>
#include <string.h>
#define PASSWORD_SIZE 20
int main() {
    char password[PASSWORD_SIZE];
    int valid = 0;
    while (!valid) {
        printf("パスワードを入力してください(8~19文字): ");
        if (fgets(password, sizeof(password), stdin) != NULL) {
            size_t len = strlen(password);
            if (len > 0 && password[len - 1] == '\n') {
                password[len - 1] = '\0';
                len--;
            } else {
                // 入力バッファをクリア
                int ch;
                while ((ch = getchar()) != '\n' && ch != EOF);
            }
            if (len >= 8 && len < PASSWORD_SIZE) {
                valid = 1;
                printf("パスワードが設定されました。\n");
            } else {
                printf("パスワードの長さが正しくありません。もう一度入力してください。\n");
            }
        } else {
            fprintf(stderr, "入力エラーが発生しました。\n");
            return 1;
        }
    }
    return 0;
}
パスワードを入力してください(8~19文字): pass
パスワードの長さが正しくありません。もう一度入力してください。
パスワードを入力してください(8~19文字): password123
パスワードが設定されました。

複数の入力を処理するメニューシステム

fgetsを用いて、ユーザーが選択するメニューシステムを実装する例です。

ユーザーの選択に応じて異なる処理を実行します。

#include <stdio.h>
#include <string.h>
#define INPUT_SIZE 10
int main() {
    char input[INPUT_SIZE];
    int running = 1;
    while (running) {
        printf("\n=== メニュー ===\n");
        printf("1. 挨拶する\n");
        printf("2. プログラムを終了する\n");
        printf("選択してください(1-2): ");
        if (fgets(input, sizeof(input), stdin) != NULL) {
            // 改行文字を削除
            size_t len = strlen(input);
            if (len > 0 && input[len - 1] == '\n') {
                input[len - 1] = '\0';
            } else {
                // 入力バッファをクリア
                int ch;
                while ((ch = getchar()) != '\n' && ch != EOF);
            }
            if (strcmp(input, "1") == 0) {
                char name[50];
                printf("名前を入力してください: ");
                if (fgets(name, sizeof(name), stdin) != NULL) {
                    size_t name_len = strlen(name);
                    if (name_len > 0 && name[name_len - 1] == '\n') {
                        name[name_len - 1] = '\0';
                    }
                    printf("こんにちは、%sさん!\n", name);
                } else {
                    fprintf(stderr, "入力エラーが発生しました。\n");
                }
            } else if (strcmp(input, "2") == 0) {
                printf("プログラムを終了します。\n");
                running = 0;
            } else {
                printf("無効な選択です。もう一度試してください。\n");
            }
        } else {
            fprintf(stderr, "入力エラーが発生しました。\n");
            running = 0;
        }
    }
    return 0;
}
=== メニュー ===

1. 挨拶する
2. プログラムを終了する

選択してください(1-2): 1
名前を入力してください: 佐藤花子
こんにちは、佐藤花子さん!
=== メニュー ===

1. 挨拶する
2. プログラムを終了する

選択してください(1-2): 3
無効な選択です。もう一度試してください。
=== メニュー ===

1. 挨拶する
2. プログラムを終了する

選択してください(1-2): 2
プログラムを終了します。

CSVファイルの解析

fgetsを使用して、CSV(カンマ区切り値)ファイルからデータを読み込み、各フィールドを解析する例です。

学生の名前と点数を読み取り、合計点を計算します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINE_SIZE 100
#define FIELD_SIZE 50
int main() {
    FILE *file = fopen("students.csv", "r");
    if (file == NULL) {
        perror("ファイルオープンエラー");
        return 1;
    }
    char line[LINE_SIZE];
    double total = 0.0;
    int count = 0;
    while (fgets(line, sizeof(line), file) != NULL) {
        // 改行文字を削除
        size_t len = strlen(line);
        if (len > 0 && line[len - 1] == '\n') {
            line[len - 1] = '\0';
        }
        char name[FIELD_SIZE];
        double score;
        // カンマで区切られたフィールドを解析
        if (sscanf(line, "%49[^,],%lf", name, &score) == 2) {
            printf("学生名: %s, 点数: %.2f\n", name, score);
            total += score;
            count++;
        } else {
            fprintf(stderr, "無効な行形式: %s\n", line);
        }
    }
    if (count > 0) {
        printf("合計点数: %.2f, 平均点数: %.2f\n", total, total / count);
    } else {
        printf("有効なデータがありません。\n");
    }
    fclose(file);
    return 0;
}
田中一郎,85.5
鈴木次郎,92.0
佐々木三郎,78.5
学生名: 田中一郎, 点数: 85.50
学生名: 鈴木次郎, 点数: 92.00
学生名: 佐々木三郎, 点数: 78.50
合計点数: 256.00, 平均点数: 85.33

コマンドライン引数の入力処理

fgetsを使用して、ユーザーがコマンドライン引数として提供した入力を処理する例です。

例えば、プログラム起動時に引数として名前を受け取り、挨拶するプログラムを作成します。

#include <stdio.h>
#include <string.h>
#define NAME_SIZE 50
int main(int argc, char *argv[]) {
    char name[NAME_SIZE];
    if (argc < 2) {
        printf("名前を入力してください: ");
        if (fgets(name, sizeof(name), stdin) != NULL) {
            size_t len = strlen(name);
            if (len > 0 && name[len - 1] == '\n') {
                name[len - 1] = '\0';
            }
        } else {
            fprintf(stderr, "入力エラーが発生しました。\n");
            return 1;
        }
    } else {
        strncpy(name, argv[1], NAME_SIZE - 1);
        name[NAME_SIZE - 1] = '\0'; // ヌル終端を確保
    }
    printf("ようこそ、%sさん!\n", name);
    return 0;
}
$ ./greet 山本太郎
ようこそ、山本太郎さん!
$ ./greet
名前を入力してください: 高橋花子
ようこそ、高橋花子さん!

ユーザー入力の履歴保存

ユーザーが入力した複数の文字列を履歴として保存し、後で参照できるようにするプログラムです。

これにより、ユーザーの入力内容を追跡・管理することが可能です。

#include <stdio.h>
#include <string.h>
#define MAX_HISTORY 10
#define INPUT_SIZE 100
int main() {
    char history[MAX_HISTORY][INPUT_SIZE];
    int count = 0;
    while (1) {
        printf("文字列を入力してください(終了するには 'exit' と入力): ");
        char input[INPUT_SIZE];
        if (fgets(input, sizeof(input), stdin) != NULL) {
            // 改行文字を削除
            size_t len = strlen(input);
            if (len > 0 && input[len - 1] == '\n') {
                input[len - 1] = '\0';
            }
            if (strcmp(input, "exit") == 0) {
                break;
            }
            // 履歴に追加
            if (count < MAX_HISTORY) {
                strncpy(history[count], input, INPUT_SIZE - 1);
                history[count][INPUT_SIZE - 1] = '\0';
                count++;
            } else {
                // 履歴が最大数に達したら最も古いエントリを削除し、新しいエントリを追加
                for (int i = 1; i < MAX_HISTORY; i++) {
                    strncpy(history[i - 1], history[i], INPUT_SIZE);
                }
                strncpy(history[MAX_HISTORY - 1], input, INPUT_SIZE - 1);
                history[MAX_HISTORY - 1][INPUT_SIZE - 1] = '\0';
            }
            printf("入力された文字列: %s\n", input);
        } else {
            fprintf(stderr, "入力エラーが発生しました。\n");
            break;
        }
    }
    printf("\n=== 入力履歴 ===\n");
    for (int i = 0; i < count; i++) {
        printf("%d: %s\n", i + 1, history[i]);
    }
    return 0;
}
文字列を入力してください(終了するには 'exit' と入力): こんにちは
入力された文字列: こんにちは
文字列を入力してください(終了するには 'exit' と入力): 世界
入力された文字列: 世界
文字列を入力してください(終了するには 'exit' と入力): exit
=== 入力履歴 ===
1: こんにちは
2: 世界

これらの実践的な例を通じて、fgetsの多様な活用方法とその安全な使用法について理解を深めることができます。

fgetsを適切に活用することで、C言語における文字列入力の安全性と効率性を大幅に向上させることが可能です。

まとめ

C言語におけるfgetsの基本的な使い方から、バッファサイズの適切な設定方法、エラーハンドリングの重要性、そして実践的な活用例までを詳しく説明しました。

これらの手法を利用することで、文字列の安全な読み込みとバッファ管理を効果的に行うことが可能です。

ぜひ、紹介した方法を実際のプログラムに取り入れ、堅牢なコード作成に挑戦してみてください。

関連記事

Back to top button