stringstream 解答ページ | Programming Place Plus 新C++編

トップページ新C++編stringstream

このページの概要

このページは、練習問題の解答例や解説のページです。



解答・解説

問題1 (確認★)

3つの整数を、空白文字で区切りながら、1つの文字列に合体するプログラムを作成してください。


たとえば、次のように書けます。

#include <iostream>
#include <sstream>
#include <string>

int main()
{
    int value1 {47};
    int value2 {25};
    int value3 {63};

    std::ostringstream oss {};
    oss << value1 << " " << value2 << " " << value3;
    std::string s {oss.str()};

    std::cout << s << "\n";
}

実行結果:

47 25 63

何個かある値を組み立てて、1つの文字列にするには、std::ostringstream を使います(本編解説)。

問題2 (基本★)

時・分・秒を “14:05:26” のような文字列で入力されるとき、時・分・秒をそれぞれ int型の変数に取り出すプログラムを作成してください。


たとえば、次のように書けます。

#include <iostream>
#include <sstream>
#include <string>

int main()
{
    std::cout << "Please enter the time. (hh:mm:ss)\n";
    std::string time {};
    std::cin >> time;

    std::istringstream iss {time};
    int hours {};
    char separation1 {};
    int minutes {};
    char separation2 {};
    int seconds {};
    iss >> hours >> separation1 >> minutes >> separation2 >> seconds;

    std::cout << "hours: " << hours << "\n";
    std::cout << "minutes: " << minutes << "\n";
    std::cout << "seconds: " << seconds << "\n";
}

実行結果:

Please enter the time. (hh:mm:ss)
14:05:26  <-- 入力された時刻
hours: 14
minutes: 5
seconds: 26

1つの文字列を分割して、複数の変数に分けるには、std::istringstream を使います(本編解説)。

時刻が “整数:整数:整数” という形式で入力されます。欲しいのは整数の部分だけですから、あいだに入る “:” 2つが邪魔です。ここでは、いらないけれども、変数に受け取ることで対処しています(separation1、separation2)。

問題3 (基本★★)

問題2のプログラムを改造して、入力された時刻の 30分後の時刻を出力するようにしてください。


30分後を計算するには、変数 minutes の値を +30 すればいいですが、単純にすると 65分とか 80分という不自然な値になってしまう可能性があります。

元の時刻が “15:45:25” なら、16時15分25秒になるべきでしょうし、“23:50:15” だったのなら、0時20分15秒になるべきでしょう。プログラムは次のようになります。

#include <iostream>
#include <sstream>
#include <string>

int main()
{
    std::cout << "Please enter the time. (hh:mm:ss)\n";
    std::string time {};
    std::cin >> time;

    std::istringstream iss {time};
    int hours {};
    char separation1 {};
    int minutes {};
    char separation2 {};
    int seconds {};
    iss >> hours >> separation1 >> minutes >> separation2 >> seconds;

    // 30分後を計算
    minutes += 30;
    if (minutes >= 60) {
        minutes %= 60;
        hours++;
        if (hours >= 24) {
            hours %= 24;
        }
    }

    std::cout << "hours: " << hours << "\n";
    std::cout << "minutes: " << minutes << "\n";
    std::cout << "seconds: " << seconds << "\n";
}

実行結果:

Please enter the time. (hh:mm:ss)
14:05:26  <-- 入力された時刻
hours: 14
minutes: 35
seconds: 26
Please enter the time. (hh:mm:ss)
15:45:25  <-- 入力された時刻
hours: 16
minutes: 15
seconds: 25
Please enter the time. (hh:mm:ss)
23:50:15  <-- 入力された時刻
hours: 0
minutes: 20
seconds: 15

変数 minutes を +30 したあと、その値が 60以上になるようなら、「時」の方へ繰り上げる処理を追加しました。「時」の方も +1 された結果、24 に達したら 0 に戻さなければなりません。

上限に達した「分」や「時」を 0~59 や 0~23 の範囲に収めるために、ここでは剰余を活用しています。代わりに減算を使ってもできます(minutes -= 60;hours -= 24;)が、上限を大幅に超えてしまっていると、正常な範囲内まで戻しきれないことがあります(125分になってしまったら、-60 では 65分になる)。この練習問題では 30分しか加算しないので、そのようなことはあり得ませんが、剰余を使う方が確実ではあります。

問題4 (応用★★★)

以下のような、改行文字を含むことによって数行文の文字列を表すようにした、1つの文字列があります。

std::string message {"aa\nbbbbbbb\nccccc\nddd\neeeeee\n"};

この文字列をもとに、各行の末尾に “.” を加えた、次のような出力を行うプログラムを作成してください。

実行結果:

aa.
bbbbbbb.
ccccc.
ddd.
eeeeee.

文字列に含まれている改行文字の個数は1個以上であれば、何個でもいいようにしてください。各行には1文字以上の文字があるものします。


文字列を改行文字ごとに分割する必要があります。分割なので、std::istringstream を使えばいいですが、改行文字の個数が決まっていないので、あらかじめいくつか変数を用意しておく方法は使えません。

ここでは、区切り文字が改行文字であることに注目します。つまり、1行ごとに処理を行えばいいということですから、std::getline関数(「文字列の入力」のページを参照)を応用できます。

std::getline関数はこれまで、std::getline(std::cin, s); というように、std::cin と組み合わせて使用しました。std::istringstream は std::cin と同じ使い方ができるので(本編解説)、std::istringstream型の変数 iss を使って、std::getline(iss, s); のようにできます。

プログラムは次のようになります。

#include <iostream>
#include <sstream>
#include <string>

int main()
{
    std::string message {"aa\nbbbbbbb\nccccc\nddd\neeeeee\n"};
    std::istringstream iss {message};

    std::string s {};
    while (true) {
        std::getline(iss, s);
        if (s == "") {
            break;
        }
        std::cout << s << ".\n";
    }
}

実行結果:

aa.
bbbbbbb.
ccccc.
ddd.
eeeeee.

while文を使った無限ループを構築し、その中で1行ずつ処理しています。

1回目の std::getline(iss, s); で変数 s に入るのは “aa” です。2回目は “bbbbbbb” が入ります。こうして得られる文字列の末尾に “.” をつけながら出力していけば目的の結果を得られます。

ループの終わらせ方が問題ですが、ここでは空文字列しか受け取れなくなったときに、break文で脱出させるようにしています。問題の条件を、「各行には1文字以上の文字があるものします。」としているので、空文字列のときに終わりとみなせますが、この条件がない場合は、“aa\n\nccccc\n” のような、内容がない行を含んでいるときに、その部分で処理を終了してしまいます。

ここまでに解説していないことですが、次のように書けば、この問題も解消します。

    std::string s {};
    while (true) {
        if (std::getline(iss, s)) {
            std::cout << s << ".\n";
        }
        else {
            break;
        }
    }

std::getline関数を使うコードを if文の条件式に入れると、その処理が完全に成功したかどうかを判定できます。こうすると、本当にもうこれ以上入力される文字列がないときにだけ、else の方に進ませることができます。空文字列の入力は、成功とみなされます。

【上級】何らかのエラーが起きたときにも else の方へ進みます。


参考リンク



更新履歴




はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る