ユーニックス総合研究所

  • home
  • archives
  • c-fgets

C言語のfgetsを使う方法

  • 作成日: 2023-01-26
  • 更新日: 2023-12-24
  • カテゴリ: C言語

C言語のfgetsの使い方

C言語のfgets関数はファイルポインタから1行読み込むときに使われます。
この関数はC言語では比較的によく使われる利用頻度の高い関数です。

この記事ではfgets関数の使い方を詳しく解説します。
この記事を読めばfgets関数の使用方法がわかります。

関連記事
C言語でcharをintに変換する方法
C言語でenumをtypedefして使う【列挙型】
C言語でforeachマクロを実装する方法
C言語でnull判定する方法【NULL, 比較】
C言語でできることを解説!C言語歴16年の開発者が語る
C言語でオブジェクト指向する【単一継承の方法】
C言語でグローバルに関数を使う方法
C言語でシャローコピーとディープコピーを実装する

fgets関数の構造

fgets関数の構造は↓のようになっています。

#include <stdio.h>  

/**  
 * streamからsizeを上限として1行をsに読み込む。  
 * EOFに達したまたはエラーの場合はNULLを返し、それ以外はsへのポインタを返す。  
 */  
char *fgets(char *s, int size, FILE *stream);  

fgets関数は3つの引数を取りchar型のポインタの返り値を返します。

引数はs, size, streamの3つです。
sは読み込んだバッファを保存する先のポインタです。
これは通常は文字配列のアドレスが渡されます。

sizeは読み込みの最大サイズです。
通常はsのサイズを渡します。

streamは読み込み元のファイルポインタです。
fopen()で開いたファイルポインタやstdinなどを指定できます。

返り値は正常の場合はsのアドレスです。
EOFに達した場合やエラーになった場合はNULLを返します。
EOFとは「End Of File」の略でファイル終端を表す概念です。

fgets関数を使ってみる

実際にfgets関数を使ってみます。

    char buf[100];  

    fgets(buf, sizeof buf, stdin);  

    printf("buf[%s]\n", buf);  
    // 123  
    // buf[123  
    // ]  

↑のコードではfgetsの第1引数には文字配列bufを指定しています。
そして第2引数にはbufのサイズ、つまりsizeofで求めた文字配列bufのバイト数を指定します。
第3引数には標準入力のファイルポインタであるstdinを指定しています。

↑のプログラムを実行すると画面が入力待ちになります。
123と入力してエンターキーを押すとその入力がbufに保存されます。

printf("buf[%s]\n", buf);bufを出力する

buf[123  
]  

と出力されます。
注目したいのはエンターキーで入力した改行もbufに保存されている点です。
このようにfgets関数は改行も一緒にバッファに保存します。

fgets関数の返り値をチェックしてみる

fgets関数の返り値をチェックしてみます。

    char buf[100];  

    char *p = fgets(buf, sizeof buf, stdin);  

    if (p == buf) {  
        printf("同じ\n");  
    }  
    // 同じ  

↑のコードを実行すると「同じ」と表示されます。
fgets関数が正常時に返すポインタのアドレス値とbufのアドレス値が同じであることがわかります。
もちろんこれはfgets関数がNULLを返した場合は違う値になるので注意が必要です。

fgets関数で複数行を読み込む

fgets関数はループ文と一緒によく使われます。
たとえばwhile文と組み合わせると↓のようになります。

    char buf[100];  

    while (fgets(buf, sizeof buf, stdin) != NULL) {  
        printf("buf[%s]\n", buf);  
    }  
    // Ctrl + D(WindowsはCtrl + Z)で終了  

↑のコードを実行すると入力待ちになります。
文字列をキーボードで入力してエンターキーを押すと再び入力待ちになります。
Ctrl + D(Windowsの場合はCtrl + Z)を押すとEOFが入力されfgets関数からNULLが返りループが終了します。

↑の場合はstdin(標準入力)から読み込んでいますがこれはもちろんfopen()で開いたファイルポインタも指定できます。
ファイル内容を1行ずつ処理したい場合などはこのようなコードで処理することができます。

ファイルを開いて1行ずつ読み込む

ファイルを開いて1行ずつ読み込みたい場合は↓のようなコードを書きます。

    // ファイルを開いて1行ずつ読み込む  
    FILE *fin = fopen("main.c", "r");  
    if (fin == NULL) {  
        perror("fopen");  
        return 1;  
    }  

    char buf[100];  

    while (fgets(buf, sizeof buf, fin) != NULL) {  
        printf("%s", buf);  
    }  

    fclose(fin);  

このコードをmain.cなどで保存します。
main関数の中に↑のコードを置いておきます。

そして実行するとmain.cのファイル内容が読み込まれます。
ループ中のprintf()では改行は出力していません。
これはfgets()で読み込んだbufに改行が含まれているからです。
改行を連続して出力することになるのでprintf()では改行は出力しません。

fgets関数のエラーの検知

fgets関数はEOFに達した場合とエラーになった場合にどちらもNULLを返します。
エラーを検知したい時はどうすればいいのでしょうか?
これはferror()を使います。

↓のコードはわざとエラーを発生させるコードです。

    // このコードはわざとエラーが発生するようになっている  
    FILE *fin = fopen("main.c", "a");  // "a"になっている  
    if (fin == NULL) {  
        perror("fopen");  
        return 1;  
    }  

    char buf[100];  
    // モード"a"のファイルから読み込む(エラー)  
    char *result = fgets(buf, sizeof buf, fin);   

    printf("result[%s]\n", result);  
    if (ferror(fin) != 0) {  
        perror("fgets");  
    }  

    fclose(fin);  
    // result[(null)]  
    // fgets: Bad file descriptor  

↑のエラーですがまずfopen()でファイルを開いています。
その時にオープンモードを「a」にしています。
aは追記モードです。ですので読み込みには対応していません。
この状態のファイルポインタをfgets()で読み込もうとするとエラーになります。

↑のコードではfgets()の返り値はNULLになっています。
これはエラーが発生していることを示しています。

ferror()はファイルポインタを調べてエラーが発生していたら0以外の値を返します。
つまり条件式ferror(fin) != 0が真になるというのはファイルポインタがエラーを抱えてる状態になります。
エラーが発生していたらperror("fgets")でエラー表示します。
perror()errnoを文字列にしてエラー表示してくれる関数です。

perror()の出力は「fgets: Bad file descriptor」になります。
「ファイルディスクリプタが変」というエラーですね。追記モードで開いているファイルなのに読み込もうとしてるからこういうエラーになったわけですね。

おわりに

今回はC言語のfgets関数の使い方を解説しました。
fgets関数はよく使いますので使い方は暗記しておいた方がいいでしょう。
なにか参考になれば幸いです。

🦝 < 一行取得!

🐭 < 読み込んでやる!