トップページ – 新C++編 – stringstream
このページは、練習問題の解答例や解説のページです。
3つの整数を、空白文字で区切りながら、1つの文字列に合体するプログラムを作成してください。
たとえば、次のように書けます。
#include <iostream>
#include <sstream>
#include <string>
int main()
{
int value1 {47};
int value2 {25};
int value3 {63};
std::ostringstream oss {};
<< value1 << " " << value2 << " " << value3;
oss std::string s {oss.str()};
std::cout << s << "\n";
}
実行結果:
47 25 63
何個かある値を組み立てて、1つの文字列にするには、std::ostringstream を使います(本編解説)。
時・分・秒を “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 {};
>> hours >> separation1 >> minutes >> separation2 >> seconds;
iss
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)。
問題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 {};
>> hours >> separation1 >> minutes >> separation2 >> seconds;
iss
// 30分後を計算
+= 30;
minutes if (minutes >= 60) {
%= 60;
minutes ++;
hoursif (hours >= 24) {
%= 24;
hours }
}
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分しか加算しないので、そのようなことはあり得ませんが、剰余を使う方が確実ではあります。
以下のような、改行文字を含むことによって数行文の文字列を表すようにした、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で書く |
RSS | 管理者情報 | プライバシーポリシー |