文字の入出力

画面に文字列を出力

C言語では主にprintf関数を用いてコンソール画面に文字列を出力していました。
C++でもprintf関数は使用可能です。
(というよりC言語の全ての関数を使用できます)

従来通りprintf関数で文字を出力しても良いですが、C++ではstd::coutによる出力方法もあります。


//C言語の入出力関数(printfなど)を
//使用するのに必要
#include <stdio.h>

//C++の入出力オブジェクトを
//使用するのに必要
#include <iostream>

int main()
{
    printf("printfによる出力\n");

    //C++的な書き方
    std::cout << "coutによる出力\n";

    getchar();
}
printfによる出力
coutによる出力

printf関数を使用するにはstdio.hをインクルードする必要があります。
これと同じように、coutを使用するにはiostreamのインクルードが必要です。

「iostream」には、input/outputのストリームを扱うために必要なデータがまとめられています。
「ストリーム」はC言語の入出力ストリームと同じで、データの入出力を「流れ」と見立てた概念です。

coutの前の「std::」については今は「そういう決まり」と考えておいてください。
(詳しくは名前空間を参照)

cout

coutは関数ではなくクラスというC++の機能です。
これについては今は理解する必要はありません。

coutは「<<」という記号を用いて文字列を出力します。
シフト演算子と同じ記号を使用しますが、機能は別物です。
「std::cout <<」に続いて出力したい文字列を指定します。
(ちなみにcoutは「シーアウト」と呼ぶことが多いです)

printf関数で変数の中身を表示するには変換指定子(書式指定文字列)を使用して引数の型を指定していましたが、coutではそのまま記述することができます。
ただし出力できるのはintやdoubleなどの基本のデータ型と一部のC++のクラスのみで、自作のデータ型(構造体など)はそのまま出力できません。
(出力できるようにする方法もあります)


#include <stdio.h>
#include <iostream>

int main()
{
    const char *str1 = "Hello";
    const char *str2 = "World";
    int number = 10;

    printf("%s %s %d\n", str1, str2, number);

    //文字列も数値もそのまま出力可能
    std::cout << str1 << " " << str2 << " " << number << "\n";

    getchar();
}
Hello World 10
Hello World 10

coutを用いるのがC++的とはいえ、printf関数の書式指定文字列の方が書きやすい場合もあるので使い分けると良いでしょう。

flush、endl

coutはflushendlといったキーワードと共に用いられることもあります。
これらも「std::」が先頭に必要です。


#include <stdio.h>
#include <iostream>

int main()
{
    std::cout << "ABCDE\n";
	
    //バッファをフラッシュする
    std::cout << "ABCDE\n" << std::flush;
	
    //改行してフラッシュする
    std::cout << "ABCDE" << std::endl;

    getchar();
}

バッファとはデータを一時的に溜めておく領域を言います。
(バッファに溜めることをバッファリングといいます)
coutなどの出力ストリームは関数の実行と同時に画面に文字を表示するのではなく、一時的にバッファにデータを溜める処理を行います。
そしてバッファが満杯になるか、バッファの掃き出しの指示があった時にデータが出力され、画面に文字が表示されます。

バッファからデータを掃き出すことをバッファのフラッシュと言います。
8行目の「std::flush」はバッファに溜まっているデータを掃き出して画面に文字を表示することを指示します。

「std::endl」もバッファのフラッシュを行いますが、最後に改行文字が挿入される点が異なります。
つまり文字列の最後に「\n」を付けなくても良いということです。

サンプルコードのような簡単な処理では速度の違いはありませんが、何千回ものループ処理で画面出力を繰り返すような場合はバッファを利用したほうが処理が早くなります。

flush、endlなどはマニピュレーターと呼ばれるものです。
printf関数の時のように文字列の書式を整えるものなど、マニピュレーターには様々な種類が存在しますが、ここでは詳しい説明は省略します。

coutの関数

coutは「<<」演算子を使用して文字を出力しますが、cout内に用意された関数を使用する方法もあります。
(メンバ関数と言います)
cout内の関数を使用するにはドット演算子を使用します。


#include <stdio.h>
#include <iostream>

int main()
{
    //一文字出力
    std::cout.put('A');
    std::cout.put('\n');

    //文字列出力
    std::cout.write("ABCDE\n", 6);
    std::cout.write("ABCDE\n", 3);

    getchar();
}
A
ABCDE
ABC

cout.putは一文字を出力する関数です。
C言語のputchar関数と同等です。

cout.writeは文字列を出力する関数です。
C言語のputs関数に似ていますが、第二引数には出力する文字数を指定します。
第一引数よりも小さい値を指定すると文字列は途中で途切れます。
反対に、大きい値を指定した場合の動作は未定義ですので避けましょう。

また、「<<」演算子による出力の場合は数値などもそのまま出力できますが、これらの関数は文字、文字列しか出力できません。

キーボード入力を受け取る

標準入力からキー入力を受け取るにはcinを用います。
(これは「シーイン」と呼びます)


#include <stdio.h>
#include <iostream>

int main()
{
    int number;
    char str[100];

    std::cin >> number;
    std::cin >> str;

    std::cout << "\nnumber: " << number << std::endl;
    std::cout << "str: " << str << std::endl;

}

cinはcoutの時とは逆に、「>>」演算子を使用します。
矢印の向きにデータが流れる、とイメージするといいでしょう。

cinもcoutと同様に、変数のデータ型に関わらずデータを受け取ることができます。
しかし、当然ながら整数型の変数に文字列を入力しようとしても受け取ることはできません。
あらかじめ用意した文字列配列の要素数以上の文字列を受け取ることもできません。
(バッファオーバーラン)
そのため、エラー処理が必要となります。

入力のエラーチェック

以下はcinによる入力の基本的なエラーチェックです。


#include <stdio.h>
#include <iostream>

int main()
{
    int number;

    while (1)
    {
        std::cout << "整数値を入力してください。" << std::endl;
        std::cin >> number;

        if (std::cin.fail())
        {
            std::cout << "入力エラー!\n" << std::endl;
            std::cin.clear();
            std::cin.ignore(1024, '\n');
            continue;
        }
        break;
    }
    
    std::cin.ignore(1024, '\n');
    std::cout << "\nnumber: " << number << std::endl;

    getchar();
}

10行目でcinによりキー入力を受け取った後、12行目でcinの状態をチェックしています。

cin.fail関数

cinはデータ受け取りの成功や失敗と言った状態を内部に持っており、キー入力を受け取ると状況に応じて状態を変化させます。
その状態をチェックするのがcin.fail関数です。
これは名前の通り、受け取りに失敗した場合に真となります。

状態を返す関数は以下があります。

good()
通常状態(エラーなし)
eof()
ファイルの終端
fail()
読み取りの失敗
bad()
その他のエラー

int型変数に数値以外の値を入力しようとすると正常に読み取れませんから、cin.fail関数は真となります。
論理否定演算子で判定を逆にしても構いません。


while (1)
{
    std::cin >> number;
    if (!std::cin.fail())
        break;

    std::cout << "入力エラー!\n" << std::endl;
    std::cin.clear();
    std::cin.ignore(1024, '\n');
}

画面にメッセージを出力した後にループの先頭に戻るのですが、その前に入力ストリームを正常な状態に戻す処理を行います。

cin.clear関数

cinが何らかのエラーの状態になった後、状態は自動的に通常状態(good)には戻りません。
状態をgoodに戻すのがcin.clear関数です。

これを忘れると、次にcinによる入力を行おうとしてもエラー状態のままです。
エラー状態ではcinは入力を受け付けず、処理がスルーされます。
その結果、無限ループに陥ってしまいます。

cin.ignore関数

入力エラーが発生した場合、その時点で入力はストップされます。
つまり、入力ストリームには読み取られなかったデータがまだ残っている可能性があります。
その状態で次にcinで入力を受け取ろうとすると、キー入力を待つことなく残っているデータが読み取られてしまいます。

これはC言語でも説明した、ストリームにデータが残る現象と同じです。
改行までを読み取る

これでは不都合なので、不要なデータは破棄しておきます。
そのために使用するのがcin.ignore関数です。

basic_istream<Elem, Tr>& ignore(
 streamsize _Count = 1,
 int_type _Delim = traits_type::eof( )
);
入力ストリームの文字を_Count文字読み捨てる。
_Countの上限に達する前に文字_Delimが出現した場合は処理を中断する。
戻り値は入力ストリーム自身。

C++では、関数にデフォルト引数という機能が追加されています。
C言語では関数が要求する引数通りに値をセットしなければ関数を呼び出すことはできませんでしたが、C++では引数を省略して関数を呼び出すことができます。
引数を省略した場合、関数側で引数にデフォルト値(規定値)がセットして処理を行います。

詳しくはデフォルト引数の項で解説します。

cin.ignore関数は、第一引数を省略して呼び出すと自動的に「1」がセットされます。
同様に、第二引数を省略すると自動的に「traits_type::eof()」がセットされます。
(これはデータの終端を表します)

入力ストリームに何文字残っているかは不明なので、第一引数に大きな値を指定して余裕を持ってデータを読み捨てておきます。
コンソール入力の最後は必ず改行文字ですから、第二引数に改行文字を指定し、改行文字を検出したら読み捨て処理を終了させます。

ややこしければ、cinによるコンソール入力でエラーが発生したら

  • std::cin.clear();
  • std::cin.ignore(1024, '\n');

と書く、と覚えておけば良いです。

ストリームの最大値を指定する

上記は1024文字目まで読み捨てていますが、それ以上が入力される可能性はあります。
それが不安ならば、std::numeric_limits<std::streamsize>::max()を指定すればストリームに入力可能な最大文字数がセットされます。


std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

文字列受け取り時のエラーチェック

文字列をchar型配列に受け取る場合はバッファオーバーランに気を付ける必要があります。
用意した配列の要素数以上に文字を受け取らないようにするにはstd::setwを使用します。


#include <stdio.h>
#include <iostream>
#include <iomanip>

int main()
{
    char str[32];

    std::cout << "文字列を入力してください。" << std::endl;

    //32文字以上の入力を制限
    std::cin >> std::setw(32) >> str;

    std::cout << "\nstr: " << str << std::endl;

    std::cin.ignore(1024, '\n');
    getchar();
}

setw

setwを使用するにはiomanipをインクルードします。

setwはstd::endlなどと同じマニピュレーターの一種です。
マニピュレーターは入出力ストリームの動作を制御します。

文字列配列へのデータ入力の前にsetwで入力文字数を指定しておくことで、それ以上の文字数の入力を防ぐことができます。
ただし、文字列配列の終端はNULL文字ですから、実際に入力されるのはマイナス1文字された文字数です。
C++のstringクラスを利用した場合は指定した数字通りの文字数が入力されます。
(stringクラスは別途説明します)

余ったデータは入力ストリームに残ったままですから、再度入力処理がある場合はcin.ignore関数で破棄しておきます。

cinの関数

coutと同じく、cinにも入力を受け取る関数が用意されています。

C++にはオーバーロードという機能があります。
これは引数が異なれば同じ関数名を複数定義できる、というものです。
(詳しくは関数のオーバーロードの項で解説します)

関数にオーバーロードが存在する場合、当サイトでは基本的に主要なものだけを解説します。
cin内に定義された関数も同名関数が多数あります。

cin.get関数


#include <iostream>

int main()
{
    int c1;
    char c2;

    std::cout << "文字を入力してください。" << std::endl;
    c1 = std::cin.get();

    std::cout << "文字を入力してください。" << std::endl;
    std::cin.ignore(1024, '\n');
    std::cin.get(c2);

    std::cout << "\n入力した文字\n";
    std::cout << (char)c1 << "\n";
    std::cout << c2 << std::endl;

    std::cin.ignore(1024, '\n');
    std::cin.get();
}
int get();
入力ストリームから一文字読み取る。
戻り値は読み取った文字。
(int型であることに注意)
basic_istream<Elem, Tr>& get(
 char c
);
入力ストリームから一文字読み取り、cに格納する。
戻り値は入力ストリーム自身。

cin.get関数は入力ストリームから一文字受け取る関数です。
C言語のgetchar関数と同等ですが、文字を引数に指定することも可能です。

今まではコードの最後に入力待機用としてC言語のgetchar関数を使用していましたが、これもcin.get関数に置き換えています。
そのため、stdio.hのインクルードは必要ありません。

実はiostreamの中でstdio.hがインクルードされているので、iostreamをインクルードすればわざわざstdio.hはインクルードしなくてもprintf関数などを使用できます。

cin.get関数にはまだオーバーロードがあります。


#include <iostream>

#define BUFFER 32

int main()
{
    char str1[BUFFER];
    char str2[BUFFER];

    std::cout << "文字列を入力してください。" << std::endl;
    std::cin.get(str1, BUFFER);

    std::cout << "文字列を入力してください。" << std::endl;
    std::cin.ignore(1024, '\n');
    std::cin.get(str2, BUFFER, '\n');

    std::cout << "\n入力した文字列\n";
    std::cout << str1 << "\n";
    std::cout << str2 << std::endl;

    std::cin.ignore(1024, '\n');
    std::cin.get();
}
basic_istream<Elem, Tr>& get(
 char *str
 streamsize _Count,
 char _Delim = '\n'
);
入力ストリームから_Count文字読み取り、strに格納する。
_Countの上限に達する前に文字_Delimが出現した場合は処理を中断する。
戻り値は入力ストリーム自身。

第一引数には文字列配列を指定します。
第二引数には文字列配列のサイズ(読み取りたい文字数)を指定します。
これで第一引数に入力ストリームから文字列を受け取ることができます。
ただし最後にNULL文字が入るため、実際には一文字分少ない文字数が読み取られます。

第三引数を省略すると、改行文字が出現するまで読み取られます。
第三引数には別の文字を指定することもできます。

cin.getline関数


#include <iostream>

#define BUFFER 32

int main()
{
    char str1[BUFFER];
    char str2[BUFFER];

    std::cout << "文字列を入力してください。" << std::endl;
    std::cin.getline(str1, BUFFER);
    if (!std::cin.good())
    {
        std::cin.clear();
        std::cin.ignore(1024, '\n');
    }

    std::cout << "文字列を入力してください。" << std::endl;
    std::cin.getline(str2, BUFFER, '\n');
    if (!std::cin.good())
    {
        std::cin.clear();
        std::cin.ignore(1024, '\n');
    }

    std::cout << "\n入力した文字列\n";
    std::cout << str1 << "\n";
    std::cout << str2 << std::endl;

    std::cin.get();
}
basic_istream<Elem, Tr>& getline(
 char *str
 streamsize _Count,
 char _Delim = '\n'
);
入力ストリームから_Count文字読み取り、strに格納する。
_Countの上限に達する前に文字_Delimが出現した場合は処理を中断する。
戻り値は入力ストリーム自身。

これはcin.get関数に引数を二つあるいは三つ指定した場合とほとんど同じ動作となります。

get関数との違いは、get関数は終端文字自身は読みとらない(入力ストリームに残る)、getline関数は終端文字を読み捨てる、という点です。

get関数は終端文字(\n)を読み取らず入力ストリームに残るので、次にget関数を使用するまでに入力ストリームを空にしておかないと何も入力できなくなる現象が発生します。

getline関数は終端文字まで読み取ります。
配列サイズ以上の文字数の入力があった場合はcinはエラー状態となりますので、状態をクリアして入力ストリームを空にしておきます。