C言語学習記録5 fgets使用時の入力ストリームのバッファ

fgets

char *fgets(char *s, int size, FILE *stream);

fgets は stream が示すファイルから改行('\n')または n-1 バイト目まで文字を読み込み、引数で渡された s に格納する。この n-1 バイトの中には改行自体も含まれる。その後文字列の末尾に終端記号 '\0' を付与する

入力に使用したテキストファイル

下記のようなテキストを使用する

abcdefghijklmnopqrstuvwxyz1234567890
aiueokakikukekosashisusesotachitsuteto

fgetsで2回入力してみる

下記のプログラムで入力する
fgetsの最大入力可能文字数は19文字とし、入力テキストの一行の文字数より少ない

#include<stdio.h>
#include<stdlib.h>    //exit()

int main(void) {

  char file_name[] = "myfile.txt";
  char buf[20];
  int i;


  FILE *fp;
  if ((fp = fopen(file_name, "rb")) == NULL) {
    perror(file_name);
    return 1;
  }


  printf("一回目\n");

  if (fgets(buf, sizeof(buf), fp) == NULL) {
    printf("入力エラー\n");
    exit(1);
  }

  for (i=0;i<sizeof(buf);i++) {
    printf("  buf[%d] -> %c : %d\n", i, buf[i], buf[i]);
  }


  if (fgets(buf, sizeof(buf), fp) == NULL) {
    printf("入力エラー\n");
    exit(1);
  }

  printf("二回目\n");

  for (i=0;i<sizeof(buf);i++) {
    printf("  buf[%d] -> %c : %d\n", i, buf[i], buf[i]);
  }

  if (fclose(fp) == EOF) {
    perror(file_name);
    return 1;
  }

  return 0;
}

実行例

一回目
  buf[0] -> a : 97
  buf[1] -> b : 98
  buf[2] -> c : 99
  buf[3] -> d : 100
  buf[4] -> e : 101
  buf[5] -> f : 102
  buf[6] -> g : 103
  buf[7] -> h : 104
  buf[8] -> i : 105
  buf[9] -> j : 106
  buf[10] -> k : 107
  buf[11] -> l : 108
  buf[12] -> m : 109
  buf[13] -> n : 110
  buf[14] -> o : 111
  buf[15] -> p : 112
  buf[16] -> q : 113
  buf[17] -> r : 114
  buf[18] -> s : 115
  buf[19] ->  : 0
二回目
  buf[0] -> t : 116
  buf[1] -> u : 117
  buf[2] -> v : 118
  buf[3] -> w : 119
  buf[4] -> x : 120
  buf[5] -> y : 121
  buf[6] -> z : 122
  buf[7] -> 1 : 49
  buf[8] -> 2 : 50
  buf[9] -> 3 : 51
  buf[10] -> 4 : 52
  buf[11] -> 5 : 53
  buf[12] -> 6 : 54
  buf[13] -> 7 : 55
  buf[14] -> 8 : 56
  buf[15] -> 9 : 57
  buf[16] -> 0 : 48
  buf[17] ->
 : 10
  buf[18] ->  : 0
  buf[19] ->  : 0

二回目の入力では一回目の入力で読みきれなかったストリームバッファに残っている分が出力する

回避策1: オーバーした分は読み飛ばす

改行文字が含まれていない場合はwhile(fgetc(fp) != '\n');で空読みする

#include<stdio.h>
#include<stdlib.h>    //exit()
#include<string.h>    // strchr(), strlen()

int main(void) {

  char file_name[] = "myfile.txt";
  char buf[20];
  int i;


  FILE *fp;
  if ((fp = fopen(file_name, "rb")) == NULL) {
    perror(file_name);
    return 1;
  }


  printf("一回目\n");

  if (fgets(buf, sizeof(buf), fp) == NULL) {
    printf("入力エラー\n");
    exit(1);
  }

  for (i=0;i<sizeof(buf);i++) {
    printf("  buf[%d] -> %c : %d\n", i, buf[i], buf[i]);
  }

  if (strchr(buf, '\n') != NULL) {
    buf[strlen(buf) - 1] = '\0';
  } else {
    // 改行文字がない場合は、bufのサイズを超えた場合のため、
    // 入力ストリームを空読みする
    while(fgetc(fp) != '\n');
  }


  if (fgets(buf, sizeof(buf), fp) == NULL) {
    printf("入力エラー\n");
    exit(1);
  }

  printf("二回目\n");

  for (i=0;i<sizeof(buf);i++) {
    printf("  buf[%d] -> %c : %d\n", i, buf[i], buf[i]);
  }

  if (fclose(fp) == EOF) {
    perror(file_name);
    return 1;
  }

  return 0;
}

実行例

一回目
  buf[0] -> a : 97
  buf[1] -> b : 98
  buf[2] -> c : 99
  buf[3] -> d : 100
  buf[4] -> e : 101
  buf[5] -> f : 102
  buf[6] -> g : 103
  buf[7] -> h : 104
  buf[8] -> i : 105
  buf[9] -> j : 106
  buf[10] -> k : 107
  buf[11] -> l : 108
  buf[12] -> m : 109
  buf[13] -> n : 110
  buf[14] -> o : 111
  buf[15] -> p : 112
  buf[16] -> q : 113
  buf[17] -> r : 114
  buf[18] -> s : 115
  buf[19] ->  : 0
二回目
  buf[0] -> a : 97
  buf[1] -> i : 105
  buf[2] -> u : 117
  buf[3] -> e : 101
  buf[4] -> o : 111
  buf[5] -> k : 107
  buf[6] -> a : 97
  buf[7] -> k : 107
  buf[8] -> i : 105
  buf[9] -> k : 107
  buf[10] -> u : 117
  buf[11] -> k : 107
  buf[12] -> e : 101
  buf[13] -> k : 107
  buf[14] -> o : 111
  buf[15] -> s : 115
  buf[16] -> a : 97
  buf[17] -> s : 115
  buf[18] -> h : 104
  buf[19] ->  : 0

二回目ではテキストの二行目が読み込まれた

回避策2: オーバーした場合はエラーにする

下記のようにエラーにする

  if (strchr(buf, '\n') != NULL) {
    buf[strlen(buf) - 1] = '\0';
  } else {
    printf("一行に入力可能な最大文字数(%d文字)をオーバーしました\n", sizeof(buf) -1 );
    exit(1);   
  }

実行例

一回目
  buf[0] -> a : 97
  buf[1] -> b : 98
  buf[2] -> c : 99
  buf[3] -> d : 100
  buf[4] -> e : 101
  buf[5] -> f : 102
  buf[6] -> g : 103
  buf[7] -> h : 104
  buf[8] -> i : 105
  buf[9] -> j : 106
  buf[10] -> k : 107
  buf[11] -> l : 108
  buf[12] -> m : 109
  buf[13] -> n : 110
  buf[14] -> o : 111
  buf[15] -> p : 112
  buf[16] -> q : 113
  buf[17] -> r : 114
  buf[18] -> s : 115
  buf[19] ->  : 0
一行に入力可能な最大文字数(19文字)をオーバーしました