文字の入出力

画面に文字列を出力

C言語ではprintf関数やputs(fputs)関数などを用いてコンソール画面に文字列を出力していました。
C++でもこれらの関数が使用可能です。
(というより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は「シーアウト」と呼ぶことが多いです。character outputの略)

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関数の書式指定文字列の方が書きやすい場合もあるので使い分けると良いでしょう。

C言語の関数はC言語用のヘッダファイル(stdio.hなど)をインクルードして使用する方法もありますが、C++用に作られたヘッダファイルをインクルードするのが一般的です。
詳しくは名前空間で改めて説明します。

このページではC言語用のヘッダファイルを使用することにします。

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」を付けなくても良いということです。

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

flushendlなどはマニピュレーターと呼ばれるものです。
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関数に似ていますが、第二引数には出力する文字数を指定します。
第一引数よりも小さい値を指定すると文字列は途中で途切れます。
反対に、大きい値を指定した場合の動作は未定義ですので避けましょう。

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

これら以外にもメンバ関数はありますが説明は省略します。

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

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


#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;

}

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

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

C++では標準入力から文字列を受け取る場合、stringクラスというものを使用するのが簡単ですが、このページはC言語学習者がC++を学習することを想定しているため、文字列をC言語風にchar型配列で受け取っています。
stringクラスを使用する場合は以下のエラーチェックは必要ありません。
ただし文字列型以外のデータ型が必要な場合は、文字列から目的の型への変換処理(および正常に変換ができたかのチェック)が必要です。

入力のエラーチェック

以下は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言語でも説明した、ストリームにデータが残る現象と同じです。
getchar関数

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

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

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

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

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

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

ややこしければ、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を使用します。
setwを使用するにはiomanipのインクルードが必要です。


#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();
}

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

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

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

cinのメンバ関数

coutと同じく、cinにも入力を受け取る関数が用意されています。
ここではその一部を紹介します。

C言語では同じ名前の関数を定義することはできませんが、C++では引数が異なる場合は同じ関数名を複数定義できます。
これを関数のオーバーロードと言います。
(詳しくは関数のオーバーロードの項で解説します)

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

cin.get関数

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

#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();
}

cin.get関数は入力ストリームから一文字受け取る関数です。
C言語のgetchar関数と同等です。

引数にchar型変数を指定すると、読み取った文字をこの変数に格納することができます。
この場合の戻り値は入力ストリーム自身という特殊なものになります。

C言語では、関数側で実引数を書き換える場合はポインタで渡す必要がありましたが、C++では参照という機能が追加されていて、引数に変数をそのまま指定しただけで関数側で実引数を書き換えることができます。
cin.get関数にchar型変数を指定する場合も、ポインタではなくそのまま指定します。

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

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

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

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


#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();
}

cin.getline関数

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

cin.getline関数はcin.get関数に引数を二つあるいは三つ指定した場合とほとんど同じ動作となります。
cin.get関数は終端文字は読み取らない(入力ストリームに残る)のに対して、cin.getline関数は終端文字を読み捨てる、という点が異なります。
バッファを空にする処理が必要ないので便利ですが、配列サイズ以上の入力があった場合のエラー処理は必要です。


#include <iostream>

#define BUFFER 8

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(std::numeric_limits<std::streamsize>::max(), '\n');
    }

    std::cout << "文字列を入力してください。" << std::endl;
    std::cin.getline(str2, BUFFER, '\n');
    if (!std::cin.good()) //何かエラー
    {
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }

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

    std::cin.get();
}