第8章の2 fgets関数とputs関数

1. fgets関数

文字列を入力するためにgetsという関数が利用されていました。けれども、この関数は入力文字数を指定できないため、用意した配列を超えて入力が可能になり、セキュリティ上危険です。そのため、現在はサポートされていません。そこで、C11からは、入力文字数を指定するgets_sという関数が用意されましたが、現時点では未実装の処理系も多いため、ここでは、fgets 関数を使って標準入力から文字列を入力する方法を説明します。

【書き方】

// 使用例
#include <stdio.h>
#include <string.h>     // strlen関数を使用するために必要

int main(void)
{
    char s[100];

    // 配列sに100文字未満の文字列を入力
    fgets(s, sizeof(s), stdin);

    // 改行文字を除去する
    size_t len = strlen(s);               // sに入力された文字列の長さ
    if (len > 0 && s[len - 1] == '\n') {  // 最終文字が改行なら
        s[len - 1] = '\0';                // '\0'で書き換える
    }
    printf("入力文字列:%s\n", s);

    return 0;
}

【実行結果例】
Good morning.
入力文字列:Good morning.

水色文字はキーボードからの入力。

fgetsはscanfの%s変換指定と異なり、スペースを読み込めます。ただし、改行’¥n’を読み込むので、’¥n’を文字列に含めたくない場合には、何らかの方法で削除する必要があります。

2. puts関数

画面に文字列を出力して復改します。

【書き方】
puts(“ABC”);  // 画面に “ABC” を表示し、その後改行する

// 例(putsとscanfの比較)
#include <stdio.h>

int main(void)
{
    char str[] = "computer";

    puts(str);              // どちらも文字列を出力し改行する
    printf("%s\n", str);    //

    return 0;
}

【実行結果例】
computer
computer

3. fgetsとputsで繰り返し文字列の入出力を行う

以下の例では、「Ctrl+Z」(UNIXの環境では「Ctrl+D」)が入力されるまで、文字列入出力を行います。

#include <stdio.h>     // 入出力関数を使用するためにインクルード
#include <string.h>    // strlen関数を使用するためにインクルード

int main(void)
{
    char s[100]; // 入力文字列を格納する配列

    puts("文字列を連続で入力してください(最大99文字)");
    puts("「Ctrl+Z」の入力で終了します。");
    while (fgets(s, sizeof(s), stdin) != NULL) { // fgets が NULL を返すまでループ
        // 改行文字を除去する
        size_t len = strlen(s);              // sに入力された文字列の長さ
        if (len > 0 && s[len - 1] == '\n') { // 最終文字が改行なら
            s[len - 1] = '\0';               // '\0'で書き換える
        }
        printf("入力文字列:%s\n", s);
    }
    return 0;
}

【実行結果例】
文字列を連続で入力してください(最大99文字)
「Ctrl+Z」の入力で終了します。
I am Shota.
入力文字列:I am Shota.
I’m a movie fan.
入力文字列:I’m a movie fan.
Let’s be friends!
入力文字列:Let’s be friends!
^Z   ← 「Ctrl+Z」(UNIXの環境では「Ctrl+D」)を入力

水色文字はキーボードからの入力。

「NULL」は空ポインタ定数で一般に「ヌル」と呼ばれ、値は一般に「0」です(処理系によっては 0 ではない場合もあります)。
この NULL は本来返却されるはずのポインタが何らかの事情で返却されないときに用いられます。
ポインタについては第10章で説明しますが、ここでは、「Ctrl+Z」が入力されると、文字列のポインタではなく空ポインタ定数の「NULL」が返却されることを理解しておいてください。

〇 演習問題

次の要領でテストの点数を入力し、それぞれの点数代の人数が何人いるか求めるプログラムを作成しなさい。

  1. テストの点数を入力するガイダンスを puts() を用いて次のように出力しなさい。
    「点数を入力しなさい。(終了条件: ‘e’ あるいは ‘E’)」
  2. fgets() を用いて、テストの点数を文字列として入力しなさい。
  3. 2. で入力した文字列が ‘e’、あるいは ‘E’ ならば処理 2~5 の処理を終了して 6 の処理を行いなさい。
  4. 文字列で入力した点数を int型変数に変換しなさい。
  5. 4. で変換した int型の点数が 0点以上 100点以下なら、次のそれぞれの点数代の人数を数えなさい。0~9点 10~19点 20~29点 30~39点 40~49点 50~59点 60~69点 70~79点 80~89点 90~99点 100点
  6. 5. の結果を表示しなさい。

【実行結果例】
点数を入力しなさい。(終了条件: ‘e’ あるいは ‘E’)
63
52
54
82
17
11
21
100
92
66
72
57
e

0~ 9点は 0人
10~19点は 2人
20~29点は 1人
30~39点は 0人
40~49点は 0人
50~59点は 3人
60~69点は 2人
70~79点は 1人
80~89点は 1人
90~99点は 1人
100点は 1人

水色文字はキーボードからの入力

解答例

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char s[128];
	int ten;
	int ninzuu[11] = {0};	  // 人数を数える配列

	puts("点数を入力しなさい。(終了条件:'e' あるいは 'E')");
	while(1) {
		fgets(s, sizeof(s), stdin);
		if (s[0] == 'e' || s[0] == 'E') {
			break;
		}
		ten = atoi(s);
		if(ten >= 0 && ten <= 100) {
			ninzuu[ten/10]++;
		}
	}
	for (int i = 0; i < 10; i++)
		printf( "%3d~%2d点は%3d人\n", i * 10, i * 10 + 9, ninzuu[i] );
	printf("100点は    %3d人\n", ninzuu[10]);

	return 0;
}
  • fgets が読み込んだ行には末尾に \n が残ります。
  • しかしその後に呼んでいる atoi(s) は
    • 先頭の空白類を読み飛ばし
    • 数字列を整数に変換した時点で処理を止める
    • そこで変換が終われば、後続の改行や文字は無視してくれる
    • という仕様なので、改行が入っていても数値変換は正しく行われます。
  • そのため、解答例では \n を取っていません。

コメント