C言語の基本|scanfで文字列を入力する

名前やキーワードをキーボードから受け取り、scanfで文字列を安全に変数へ保存する基本を身につけよう。

整数や小数、1文字をscanfで入力できるようになると、次に使いたくなるのが文字列の入力です。

実際のプログラムでは、数値だけではなくさまざまな文字列を扱います。

たとえば、

  • ユーザー名
  • ニックネーム
  • 検索キーワード
  • コマンド
  • ファイル名

などです。

C言語では、文字列をchar型の配列に格納します。そしてscanfで文字列を読み取るときには、%sを使用します。

ただし、文字列入力には整数入力とは少し違った注意点があります。

特に押さえておきたいのは、次の3点です。

  • 文字列を保存するにはchar型の配列を用意する
  • %sでは入力できる文字数を制限する
  • %sは空白を見つけると、そこで文字列の読み取りを終了する

さらに、int型などの変数へ入力するときとは異なり、char型の配列名をscanfへ渡すときには通常&を付けません。

ここでは、scanfを使って文字列を入力し、その内容をprintfで表示できるところまでを順番に見ていきます。

C言語の文字列はchar型の配列で扱う

C言語には、文字列そのものを直接表す基本データ型はありません。

文字列は、char型の文字が連続して並んだデータとして扱います。

たとえば、名前やキーワードを保存するためには次のような配列を用意します。

char name[50];

この宣言では、char型の要素を50個持つ配列nameを用意しています。

文字列を入力すると、この配列の中へ1文字ずつ順番に格納されます。

たとえばSuzukiという文字列なら、イメージとしては次のようになります。

'S' 'u' 'z' 'u' 'k' 'i' '\0'

最後にある\0はナル文字と呼ばれ、C言語では文字列の終わりを表します。

つまり、文字列を保存するときは入力された文字だけでなく、最後の\0を保存する場所も必要です。

図1:入力した文字列がchar型配列へ格納される仕組み

この図から分かること

C言語の文字列は、1つの大きなデータとして保存されるのではなく、char型の配列へ1文字ずつ並べて格納されます。

たとえばlinuxを入力すると、

l
i
n
u
x

という5文字に加えて、その後ろへ文字列の終わりを示す\0が格納されます。

つまり、5文字の文字列でも実際には6個の配列要素が必要です。

この仕組みが分かると、文字列用の配列を用意するときに、入力した文字数ぴったりではなく**\0を保存するための1要素が必要になる理由**が理解しやすくなります。

配列サイズには\0の分も必要

次の配列を考えてみましょう。

char name[50];

配列には50個のchar型要素があります。

ただし、文字列を格納するときには最後の1要素を\0のために残しておく必要があります。

そのため、安全に格納できる文字列は最大49文字です。

配列サイズ        50要素
入力できる文字数  最大49文字
残り              \0用の1要素

同じ考え方で、100要素の配列なら最大99文字の文字列を格納できます。

配列の宣言配列サイズ文字列として使用できる最大文字数
char str[10];109
char str[30];3029
char str[40];4039
char str[50];5049
char str[100];10099

基本的には、

配列サイズ N
↓
文字列として入力する最大文字数 N - 1
↓
残り1要素を\0に使用

と考えると分かりやすいでしょう。

scanfで文字列を入力するときは%sを使う

scanfで文字列を読み取るときには、変換指定として%sを使用します。

基本的な形は次のようになります。

scanf("%s", str);

たとえば次の配列があるとします。

char name[50];

文字列を入力するだけなら、

scanf("%s", name);

という形になります。

ただし、このままでは入力される文字数を制限できません。

そのため、実際には後ほど説明するフィールド幅を指定して、

scanf("%49s", name);

のように書くほうが安全です。

文字列入力では配列名に&を付けない

scanfでint型の変数へ入力するときは、次のように&を付けました。

int number;

scanf("%d", &number);

一方、文字列を格納するchar型配列の場合は次のように書きます。

char name[50];

scanf("%49s", name);

nameの前には&がありません。

違いを整理すると次のようになります。

入力先scanfへの指定
int型の変数変数のアドレスを&で取得する&number
double型の変数変数のアドレスを&で取得する&value
char型の1文字変数のアドレスを&で取得する&ch
char型の配列配列名をそのまま指定するname

C言語では、配列名を式の中で使用すると、多くの場合は配列の先頭要素を指すアドレスとして扱われます

そのため、

scanf("%49s", name);

と書けば、scanfは文字列を書き込み始める場所を知ることができます。

int型との違いを比べてみよう

int型なら次のようになります。

int number;

scanf("%d", &number);

numberだけではなく、&numberを指定します。

文字列の場合は、

char name[50];

scanf("%49s", name);

となります。

イメージとしては、

int number;
↓
numberの場所を渡したい
↓
&number

です。

一方、配列では、

char name[50];
↓
nameを使うと配列の先頭要素を指すアドレスとして扱われる
↓
nameをそのままscanfへ渡す

となります。

scanfによる文字列入力では、

char型配列の名前には通常&を付けない

と覚えておくとよいでしょう。

%sには入力できる文字数を指定する

文字列入力で特に重要なのが、入力できる最大文字数を制限することです。

たとえば次の配列があります。

char name[20];

ここへ次のように入力するとします。

scanf("%s", name);

%sだけでは、入力文字数の上限を指定していません。

ユーザーが配列へ収まらないほど長い文字列を入力すると、配列の範囲を越えて書き込もうとする危険があります。

このような問題を防ぐため、%sにはフィールド幅を指定します。

配列が20要素なら、

scanf("%19s", name);

とします。

19は、scanfが読み取る文字数の上限です。

残りの1要素を文字列末尾の\0のために確保できます。

フィールド幅は配列サイズより1小さくする

基本的な考え方は非常にシンプルです。

配列サイズがN
↓
%sのフィールド幅はN - 1

たとえば、

char keyword[40];

なら、

scanf("%39s", keyword);

です。

同じように、

char nickname[30];

なら、

scanf("%29s", nickname);

とします。

対応を表にすると次のようになります。

配列scanfの指定最大入力文字数
char text[10];scanf("%9s", text);9
char name[20];scanf("%19s", name);19
char nickname[30];scanf("%29s", nickname);29
char keyword[40];scanf("%39s", keyword);39
char word[50];scanf("%49s", word);49

文字列入力では、

配列サイズとフィールド幅をセットで確認する

習慣を付けておくとよいでしょう。

なぜ文字数の制限が必要なのか

配列にはあらかじめ決められた数の要素しかありません。

たとえば、

char keyword[10];

なら10要素です。

ここへ9文字まで入力するなら、

入力文字 9要素
+
\0        1要素
=
合計     10要素

となり、配列へ収まります。

ところが、それより長い文字列を無制限に書き込もうとすると、本来keywordに割り当てられていない領域まで書き換えてしまう可能性があります。

これをバッファオーバーフローと呼びます。

バッファオーバーフローは、プログラムの誤動作やメモリ内容の破壊につながる可能性があります。

そのため、scanfで%sを使用するときは、配列の大きさに合わせてフィールド幅を指定することが大切です。

図2:%sのフィールド幅で文字列を安全に入力する

この図から分かること

char型配列へ文字列を入力するときは、配列の大きさより1少ない値をscanfのフィールド幅に指定します。

たとえば、

char name[10];

なら、

scanf("%9s", name);

とします。

これによってscanfが読み取る文字列は最大9文字に制限され、最後に\0を格納するための1要素を残せます。

一方、

scanf("%s", name);

だけでは入力文字数を制限できません。

長すぎる文字列が入力された場合、配列の外側まで書き込んでしまう危険があります。

文字列入力では、

配列サイズ
↓
1を引く
↓
%sのフィールド幅として指定

という考え方を身につけておきましょう。

%sは空白までしか読み取らない

%sには、もう1つ重要な特徴があります。

%sは文字列を読み取るときに、空白文字を区切りとして扱います

ここでいう空白文字には、たとえば次のようなものがあります。

  • 半角スペース
  • タブ
  • 改行

たとえば、

Hello World!

と入力したとします。

scanfで、

scanf("%49s", text);

と読み取った場合、textへ格納されるのは、

Hello

までです。

スペースを見つけたところで%sによる読み取りが終了するからです。

空白より後ろの文字はどうなるのか

たとえば、

hello world

と入力し、

scanf("%19s", word);

を実行すると、wordへ入るのは、

hello

です。

その後ろにある、

world

は、このscanfでは読み取られません。

後続の入力処理へ影響することがあるため、%sでは空白を含む文章全体を読み取れないという特徴を理解しておくことが大切です。

空白を含む行を読み取りたい場合には、fgetsなど別の方法を使用します。

ここではまず、

%s
↓
空白のない1つの単語を読むのに向いている

と理解しておけば十分です。

%sが向いている入力

%sは、空白を含まない文字列を読み取る場合に使いやすい方法です。

たとえば次のような入力です。

linux
suzuki
report.txt
search

一方、次のような空白を含む入力には向いていません。

hello world
Taro Suzuki
C language

%sの性質を知ったうえで、用途に合わせて使うことが大切です。

scanfで検索キーワードを入力してみよう

それでは、scanfの%sを使って文字列を入力するプログラムを作ってみましょう。

ここでは、検索キーワードを入力し、その内容をそのまま表示します。

検索キーワードを入力して表示するプログラム

ファイル名:5_9_1.c

#include <stdio.h>

int main(void)
{
    char search_word[50];

    /* 検索キーワードを入力する */
    printf("検索する単語を入力してください > ");
    scanf("%49s", search_word);

    /* 入力された文字列を表示する */
    printf("入力された検索語: %s\n", search_word);

    return 0;
}
実行結果の例
検索する単語を入力してください > network
入力された検索語: network

このプログラムでは、まず文字列を保存するために次の配列を用意しています。

char search_word[50];

search_wordは50要素のchar型配列です。

文字列の最後には\0が必要なので、入力する文字列は最大49文字に制限します。

そのため、scanfは次のようになっています。

scanf("%49s", search_word);

search_word[50]は文字列を保存する場所

最初に次の宣言があります。

char search_word[50];

これは、検索キーワードを保存するための配列です。

50要素のうち、最大49要素を入力文字に使用し、残り1要素を\0に使用します。

search_word[50]
↓
最大49文字を入力
+
最後に\0

たとえばnetworkを入力した場合は、イメージとして次のように格納されます。

'n' 'e' 't' 'w' 'o' 'r' 'k' '\0'

%49sで最大49文字に制限する

文字列を読み取っている部分は次のコードです。

scanf("%49s", search_word);

%49sは、

文字列を最大49文字まで読み取る

という意味です。

search_wordは50要素なので、

49文字
+
\0
=
最大50要素

となり、配列サイズに収まるようにしています。

文字列入力を書くときは、

char search_word[50];
scanf("%49s", search_word);

のように、配列サイズとフィールド幅を対応させて確認しましょう。

配列名search_wordには&を付けない

scanfの2つ目の指定は次のようになっています。

search_word

&search_wordではありません。

コード全体では、

scanf("%49s", search_word);

です。

char型の配列名search_wordを式の中で使うと、多くの場合、配列の先頭要素へのアドレスとして扱われます。

そのため、文字列を書き込む場所としてsearch_wordをそのままscanfへ渡せます。

これまでの整数入力と比較すると分かりやすくなります。

int number;
scanf("%d", &number);

int型では&numberです。

文字列では、

char search_word[50];
scanf("%49s", search_word);

となります。

違いを混同しないようにしましょう。

printfでも%sを使って文字列を表示する

入力した文字列を表示している部分は次のコードです。

printf("入力された検索語: %s\n", search_word);

printfでも文字列を表示するときは%sを使用します。

今回の実行例ではnetworkを入力しているため、

入力された検索語: network

と表示されます。

scanfとprintfの両方で%sが登場しますが、それぞれの役割は異なります。

関数%sの役割
scanf文字列を入力する
printf文字列を表示する

空白を含む文字列を入力するとどうなるのか

先ほどのプログラムで、次のように入力したとします。

computer network

scanfは、

scanf("%49s", search_word);

なので、最初の空白まで読み取ります。

search_wordへ格納されるのは、

computer

です。

そのため表示結果は、

入力された検索語: computer

となります。

networkまで同じ文字列として読み取るわけではありません。

これは%sの重要な特徴です。

図3:%sは空白を見つけると読み取りを終了する

この図から分かること

%sは、空白を含まない単語を入力するときには便利ですが、空白を含む文章をそのまま読み取ることはできません。

たとえば、

hello world

を入力した場合は、

hello

までを読み取ります。

途中のスペースが区切りになるためです。

つまり、

入力
hello world
    ↓
%s
    ↓
hello

となります。

空白を含む行全体を入力する場合には別の方法が必要ですが、この段階では**%sは空白までを1つの文字列として読み取る**と覚えておきましょう。

scanfでよく使う変換指定を確認しよう

ここまでで、整数、小数、1文字、文字列の入力方法が登場しました。

scanfでよく利用する変換指定を整理すると、次のようになります。

変換指定読み取る内容対応する型の例入力例
%d10進整数int123
%u符号なし10進整数unsigned int123
%o8進整数unsigned int17、075
%x16進整数unsigned int7F、a9
%f実数float3.14
%lf実数double3.14159
%c1文字charA
%s文字列char型配列Hello

特に、最初のうちによく使う組み合わせは次の4つです。

int
↓
%d

double
↓
%lf

char
↓
%c

char型配列
↓
%s

文字列の場合は、さらに配列サイズに合わせたフィールド幅を付けます。

たとえば、

char name[30];

なら、

scanf("%29s", name);

です。

実践問題:4種類のデータを入力してみよう

ここまで学んだ文字列、1文字、整数、実数の入力を組み合わせて確認してみましょう。

次のプログラムは、ユーザーから次の4つを入力してもらいます。

  • ユーザー名
  • 好きなアルファベット1文字
  • 学習年数
  • 目標学習時間

空欄の①~④へ適切なscanfを書き、実行結果の例のように動くようにしてください。

ユーザー情報を入力して表示する実践問題

ファイル名:5_9_2.c

#include <stdio.h>

int main(void)
{
    char favorite_letter;
    int study_years;
    double target_hours;
    char username[30];

    /* データを入力する */
    printf("ユーザー名を入力してください > ");
    ①

    printf("好きなアルファベットを1文字で入力してください > ");
    ②

    printf("学習年数を入力してください > ");
    ③

    printf("目標学習時間を入力してください(単位:時間)> ");
    ④

    /* 入力されたデータを表示する */
    printf("\n入力された内容:\n");
    printf("ユーザー名        : %s\n", username);
    printf("好きな文字        : %c\n", favorite_letter);
    printf("学習年数          : %d\n", study_years);
    printf("目標学習時間      : %.1f 時間\n", target_hours);

    return 0;
}
実行結果の例
ユーザー名を入力してください > tanaka
好きなアルファベットを1文字で入力してください > K
学習年数を入力してください > 2
目標学習時間を入力してください(単位:時間)> 450.5

入力された内容:
ユーザー名        : tanaka
好きな文字        : K
学習年数          : 2
目標学習時間      : 450.5 時間

この問題では、4種類の変数が使われています。

変数入力する内容
usernamechar型配列文字列
favorite_letterchar1文字
study_yearsint整数
target_hoursdouble実数

それぞれの型に対応するscanfの変換指定を考えてみましょう。

実践問題の解答例

ユーザー情報を4種類の型へ入力するプログラム

ファイル名:5_9_3.c

#include <stdio.h>

int main(void)
{
    char favorite_letter;
    int study_years;
    double target_hours;
    char username[30];

    /* データを入力する */
    printf("ユーザー名を入力してください > ");
    scanf("%29s", username);

    printf("好きなアルファベットを1文字で入力してください > ");
    scanf(" %c", &favorite_letter);

    printf("学習年数を入力してください > ");
    scanf("%d", &study_years);

    printf("目標学習時間を入力してください(単位:時間)> ");
    scanf("%lf", &target_hours);

    /* 入力されたデータを表示する */
    printf("\n入力された内容:\n");
    printf("ユーザー名        : %s\n", username);
    printf("好きな文字        : %c\n", favorite_letter);
    printf("学習年数          : %d\n", study_years);
    printf("目標学習時間      : %.1f 時間\n", target_hours);

    return 0;
}
実行結果の例
ユーザー名を入力してください > tanaka
好きなアルファベットを1文字で入力してください > K
学習年数を入力してください > 2
目標学習時間を入力してください(単位:時間)> 450.5

入力された内容:
ユーザー名        : tanaka
好きな文字        : K
学習年数          : 2
目標学習時間      : 450.5 時間

① 文字列は%sで読み取り、配列サイズに合わせて幅を指定する

最初はユーザー名の入力です。

scanf("%29s", username);

usernameは次のように宣言されています。

char username[30];

30要素の配列なので、入力できる文字列を最大29文字に制限しています。

配列サイズ
30
↓
入力文字数
最大29文字
↓
残り1要素
\0

そのため、

scanf("%29s", username);

という組み合わせになります。

また、usernameはchar型の配列なので、配列名の前に&は付けません。

scanf("%29s", username);

が正しい形です。

② 1文字の入力では %c の前に半角スペースを入れる

好きなアルファベットを入力する部分は次のコードです。

scanf(" %c", &favorite_letter);

favorite_letterはchar型の変数です。

char favorite_letter;

1文字なので%cを使用します。

そして、%cの前には半角スペースがあります。

" %c"

この先頭のスペースによって、それまでの入力で残っている改行や空白などを読み飛ばしてから、次の1文字を読み取れます。

そのため、

scanf(" %c", &favorite_letter);

という形にしておくと、前に別のscanfがある場合でも扱いやすくなります。

char型の1変数へ書き込むので、favorite_letterの前には&が必要です。

③ int型の入力では%dを使う

学習年数はint型です。

int study_years;

そのため、scanfでは%dを使います。

scanf("%d", &study_years);

たとえば2を入力すると、

2を入力
↓
%dで10進整数として読み取る
↓
study_yearsへ2を格納

となります。

int型の変数へ値を書き込むため、study_yearsの前には&を付けます。

&study_years

表示するときも%dを使用しています。

printf("学習年数          : %d\n", study_years);

④ double型の入力では%lfを使う

目標学習時間はdouble型です。

double target_hours;

double型へのscanf入力では%lfを使用します。

scanf("%lf", &target_hours);

ここでは%fではなく**%lf**であることに注意しましょう。

入力例が、

450.5

なら、450.5がdouble型のtarget_hoursへ格納されます。

表示部分では、

printf("目標学習時間      : %.1f 時間\n", target_hours);

となっています。

printfではdouble型を%fで表示できます。

さらに今回は%.1fとしているため、小数点以下1桁まで表示します。

そのため、

450.5

と表示されます。

4種類の入力方法を並べて確認する

実践問題で使用したscanfを並べると、型ごとの違いがよく分かります。

scanf("%29s", username);
scanf(" %c", &favorite_letter);
scanf("%d", &study_years);
scanf("%lf", &target_hours);

対応関係を整理すると次のようになります。

入力するデータ変数scanf
ユーザー名usernamechar型配列scanf("%29s", username);
好きな文字favorite_lettercharscanf(" %c", &favorite_letter);
学習年数study_yearsintscanf("%d", &study_years);
目標学習時間target_hoursdoublescanf("%lf", &target_hours);

特に文字列だけは、他の3つと変数の渡し方が違います。

char型配列
username
↓
usernameをそのまま渡す

それ以外は、

char
int
double
↓
&変数名

となっています。

文字列入力では配列サイズと%sをセットで確認する

scanfで文字列を入力するときに特に意識したいのは、次の組み合わせです。

char username[30];
scanf("%29s", username);

配列サイズが30ならフィールド幅は29です。

同じように、

char keyword[40];
scanf("%39s", keyword);

となります。

確認するときは、

1. char型配列のサイズを見る
2. サイズから1を引く
3. その値を%sのフィールド幅にする
4. 配列名には&を付けない

という順番で考えると分かりやすいでしょう。

そして、%sでは空白までしか読み取れないため、

linux
network
suzuki
report.txt

のような空白を含まない文字列の入力に向いています。

まずは、

char word[30];
scanf("%29s", word);
printf("%s\n", word);

という基本形を自分で動かし、入力した文字列がchar型配列へ格納され、その内容を%sで表示できることを確認してみると、C言語の文字列入力の仕組みがつかみやすくなります。