文字列から数値、数値から文字列への変換は、ユーザー入力の処理やファイル入出力、ログ出力などで頻繁に登場します。
本記事ではC++のstd::stoi
、std::stod
、std::to_string
を中心に、初心者の方がつまずきやすい点を避けながら、正しく使いこなすための基本と実践方法を解説します。
C++の文字列⇔数値変換の基本
使う型(int, double, std::string)の確認
C++で文字列と数値の相互変換を扱う際、まず関係する代表的な型を整理します。
整数はint
、浮動小数点数はdouble
、文字列はstd::string
を基本として扱います。
整数が非常に大きい場合はlong long
、より高精度の小数が必要な場合はlong double
などもありますが、入門ではint
とdouble
で十分です。
次の表は用途の目安です。
用途 | 推奨型 | 補足 |
---|---|---|
カウント、インデックス、年齢など | int | 実装依存ですが多くの環境で32bitです |
金額や比率の概算、実数計算 | double | 丸め誤差に注意が必要です |
文字データの保持、入出力 | std::string | UTF-8文字列も保持できます |
型を正しく選ぶことが変換の正確さと安全性につながります。
変換が必要になる場面(入出力・計算)
ユーザーから標準入力やファイルで受け取る値は多くの場合文字列です。
計算に使うには一度数値型へ変換してから演算し、結果を画面やログに出す際に再び文字列へ戻すという流れになります。
GUIやWeb連携でも同様で、境界(入力/出力)で文字列、内部で数値という役割分担が基本です。
文字列→数値: std::stoi/std::stodの使い方
std::stoiの基本(例と戻り値)
std::stoi
は文字列をint
に変換します。
成功すれば整数値を返し、必要なら解析済み文字数を取得できます。
#include <iostream>
#include <string>
int main() {
std::string s1 = "42";
std::string s2 = " -15xyz"; // 先頭空白と符号、末尾に余分な文字
std::size_t pos = 0; // どこまで読んだかを受け取る位置
int a = std::stoi(s1); // "42" → 42
int b = std::stoi(s2, &pos, 10); // 基数10で変換。posに消費した文字数が入る
std::cout << "a = " << a << "\n";
std::cout << "b = " << b << ", pos = " << pos << "\n";
std::cout << "未処理の残り = '" << s2.substr(pos) << "'\n";
// 基数0なら "0x" や先頭の0を自動判定(16進/8進/10進)
std::string hex = "0xFF";
int c = std::stoi(hex, nullptr, 0);
std::cout << "c = " << c << "\n";
}
a = 42
b = -15, pos = 5
未処理の残り = 'xyz'
c = 255
第2引数のpos
で「どこまでが数値として読めたか」を知ることで、余分な文字が混じっていないか検査できます。
std::stodの基本(例と戻り値)
std::stod
は文字列をdouble
へ変換します。
指数表記も扱えます。
#include <iostream>
#include <string>
int main() {
std::string s1 = "3.14159";
std::string s2 = " +2.5e2 rest"; // 先頭空白、符号、指数表記、末尾に余分な文字
std::size_t pos = 0;
double x = std::stod(s1); // 3.14159
double y = std::stod(s2, &pos); // 2.5e2 → 250.0
std::cout << "x = " << x << "\n";
std::cout << "y = " << y << ", pos = " << pos << "\n";
std::cout << "未処理の残り = '" << s2.substr(pos) << "'\n";
}
x = 3.14159
y = 250, pos = 8
未処理の残り = ' rest'
浮動小数点の変換はロケールに依存せず「小数点はピリオド(.)」です。
カンマ(,)は区切りとして扱われません。
変換失敗時の注意点(エラーと例外)
std::stoi
/std::stod
は失敗時に例外を投げます。
パースする前提の文字列が本当に数値か、範囲内かを想定して、try
/catch
で守るのが安全です。
#include <iostream>
#include <string>
#include <stdexcept>
int main() {
for (std::string s : {"abc", "9999999999999999999999"}) {
try {
int v = std::stoi(s);
std::cout << "成功: " << v << "\n";
} catch (const std::invalid_argument& e) {
std::cout << "invalid_argument: 数値として解釈できません (" << s << ")\n";
} catch (const std::out_of_range& e) {
std::cout << "out_of_range: intの範囲を超えています (" << s << ")\n";
}
}
}
invalid_argument: 数値として解釈できません (abc)
out_of_range: intの範囲を超えています (9999999999999999999999)
「異常時は例外が発生する」点を忘れると、プログラムが突然終了する原因になります。
入力の前後空白や符号の扱い
std::stoi
/std::stod
は先頭の空白と符号(+/-)を自動的に受け付けます。
ただし末尾に余分な文字があっても途中までの数値だけ読んで成功してしまうため、pos
で全消費か確認すると堅牢です。
#include <iostream>
#include <string>
bool parse_int_strict(const std::string& s, int& out) {
std::size_t pos = 0;
try {
int v = std::stoi(s, &pos, 10);
// 全文字が消費されていれば厳密に数値とみなす
if (pos == s.size()) {
out = v;
return true;
}
return false;
} catch (...) {
return false;
}
}
int main() {
for (std::string s : {" +123", "45x", " -7 "}) {
int v = 0;
bool ok = parse_int_strict(s, v);
std::cout << "'" << s << "' -> ok=" << std::boolalpha << ok
<< (ok ? ", value=" + std::to_string(v) : "") << "\n";
}
}
' +123' -> ok=true, value=123
'45x' -> ok=false
' -7 ' -> ok=false
末尾の空白は「余分な文字」として扱われるため、必要に応じてトリム(前後空白の除去)してから変換するか、パーサを工夫します。
数値→文字列: std::to_stringの使い方
整数を文字列に変換(int → std::string)
std::to_string
は数値を手軽にstd::string
へ変換します。
ログメッセージやIDの連結などで簡潔に使えるのが利点です。
#include <iostream>
#include <string>
int main() {
int id = 1234;
std::string msg = "ID=" + std::to_string(id); // 連結も簡単
std::cout << msg << "\n";
}
ID=1234
小数を文字列に変換(double → std::string)
浮動小数点数も同様に変換できます。
#include <iostream>
#include <string>
int main() {
double pi = 3.1415926535;
std::string s = std::to_string(pi);
std::cout << s << "\n"; // 多くの実装で小数点以下6桁相当の出力になります
}
実行結果例(典型):
3.141593
出力の見た目は実装に依存しますが、多くの環境で固定桁数(例: 6桁)になりがちです。
小数の桁数の注意点(to_stringの出力)
std::to_string
は小数点以下の桁数を直接指定できません。
見た目を制御したい場合はstd::ostringstream
と<iomanip>のstd::fixed
/std::setprecision
を使います。
#include <iostream>
#include <sstream>
#include <iomanip>
#include <string>
int main() {
double rate = 0.123456789;
// to_string は手軽だが桁数は制御しにくい
std::cout << "to_string: " << std::to_string(rate) << "\n";
// 書式を指定して文字列化する
std::ostringstream oss;
oss << std::fixed << std::setprecision(3) << rate; // 小数点以下3桁
std::string s = oss.str();
std::cout << "formatted: " << s << "\n";
}
to_string: 0.123457
formatted: 0.123
金額など表示桁数が重要な場合はstd::to_string
だけに頼らず、明示的に書式を指定してください。
実用サンプルとチェックリスト
文字列→数値→計算→文字列の一連の流れ
税込価格を文字列で受け取り、消費税率を適用して合計を出し、所定の桁数で文字列化する例です。
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <stdexcept>
// 前後空白を取り除くユーティリティ(簡易版)
std::string trim(const std::string& s) {
const auto first = s.find_first_not_of(" \t\r\n");
if (first == std::string::npos) return "";
const auto last = s.find_last_not_of(" \t\r\n");
return s.substr(first, last - first + 1);
}
int main() {
// 入力(本来は std::getline(std::cin, ...) などで受け取る)
std::string price_str = " 1980 "; // 価格(円)
std::string tax_str = " 0.10 "; // 税率(10%なら0.10)
try {
// 1) 文字列を前後トリム
price_str = trim(price_str);
tax_str = trim(tax_str);
// 2) 数値へ変換
std::size_t p1 = 0, p2 = 0;
int price = std::stoi(price_str, &p1);
double tax = std::stod(tax_str, &p2);
// 全消費チェックで混入文字を検出
if (p1 != price_str.size() || p2 != tax_str.size()) {
throw std::invalid_argument("余分な文字を検出しました");
}
// 3) 計算
double total = price * (1.0 + tax);
// 4) 小数2桁で表示用文字列を作る
std::ostringstream oss;
oss << std::fixed << std::setprecision(2) << total;
std::string total_str = oss.str();
std::cout << "税込金額: " << total_str << " 円\n";
// 5) ログやID連結など、整数は to_string が手軽
std::string log = "price=" + std::to_string(price) + ", tax=" + std::to_string(static_cast<int>(tax * 100)) + "%";
std::cout << log << "\n";
} catch (const std::exception& e) {
std::cerr << "入力エラー: " << e.what() << "\n";
return 1;
}
}
税込金額: 2178.00 円
price=1980, tax=10%
入力の正当性検証、変換、計算、書式化の4段階を意識すると、バグの混入を防ぎやすくなります。
よくあるミス(全角数字・余分な文字・範囲外)
実務で頻発する落とし穴を把握しておくと、デバッグ時間を大幅に節約できます。
症状 | 例 | 原因 | 対策 |
---|---|---|---|
全角数字で失敗 | “123” | std::stoi はASCII数字のみ解釈 | 入力を正規化する、全角→半角変換を行う |
末尾に余分な文字 | “45kg” | 数値の後ろを読まずに成功することがある | pos で全消費確認、またはトリム/バリデーション |
範囲外 | “999999999999” | int の範囲超過でstd::out_of_range | long long 系(std::stoll )を検討、例外処理を追加 |
小数点がカンマ | “3,14” | 小数点はピリオドのみ | 事前置換やロケール対応のパーサを利用 |
期待桁で表示されない | “3.140000” | std::to_string は桁数制御不可 | ostringstream でsetprecision 指定 |
「パースできた=正しい入力」とは限りません。
残り文字の有無、範囲、表記揺れをチェックすることが重要です。
まとめ
本記事ではstd::stoi
とstd::stod
での文字列→数値変換、std::to_string
での数値→文字列変換の基本を解説しました。
例外処理とpos
による検証で入力の健全性を確保し、表示はostringstream
で桁数を明示するのが実践的な指針です。
初心者のうちは、余分な文字や範囲外、全角数字といった落とし穴をサンプルのように丁寧に検査しながら、安全で読みやすいコードを心がけてください。