C++では、数値を文字列に変換したり、文字列を数値として読み取ったりする方法が複数用意されています。
代表的な方法は次のとおりです。
- 数値から文字列:
std::to_string、std::format、std::to_chars - 文字列から数値:
std::stoi系関数、std::from_chars - ストリームを利用した変換:
std::ostringstream、std::istringstream - C言語由来の関数:
strtol、strtodなど
簡単な変換であればstd::to_stringやstd::stoiが使いやすく、厳密な入力検証や大量のデータを処理する場合にはstd::from_charsやstd::to_charsが適しています。
数値から文字列へ変換する方法
std::to_stringを使う
std::to_stringは、数値を簡単にstd::stringへ変換できる関数です。
#include <iostream>
#include <string>
int main()
{
int number = 123;
std::string text = std::to_string(number);
std::cout << text << '\n';
}
実行結果は次のとおりです。
123
std::to_stringは、主に次の型に対応しています。
int
long
long long
unsigned int
unsigned long
unsigned long long
float
double
long double
整数だけでなく、浮動小数点数も変換できます。
#include <iostream>
#include <string>
int main()
{
int intValue = 100;
double doubleValue = 3.14159;
std::string intText = std::to_string(intValue);
std::string doubleText = std::to_string(doubleValue);
std::cout << intText << '\n';
std::cout << doubleText << '\n';
}
C++23以前では、浮動小数点数を変換すると、一般的に次のような形式になります。
100
3.141590
C++23以前のstd::to_stringでは、浮動小数点数が通常、小数点以下6桁程度で出力されます。
ただし、C++26ではstd::to_stringの仕様が変更されており、浮動小数点数の出力結果がC++23以前と異なる可能性があります。
そのため、小数点以下の桁数や表示形式を確実に指定したい場合は、std::formatやstd::ostringstreamを使用する方が適切です。
std::formatで表示形式を指定する
C++20以降では、std::formatを使って書式を指定しながら文字列を作成できます。
#include <format>
#include <iostream>
#include <string>
int main()
{
double value = 3.1415926535;
std::string text = std::format("{:.2f}", value);
std::cout << text << '\n';
}
実行結果は次のとおりです。
3.14
代表的な書式指定には、次のようなものがあります。
std::format("{}", 123); // "123"
std::format("{:.2f}", 3.14159); // "3.14"
std::format("{:08}", 123); // "00000123"
std::format("{:x}", 255); // "ff"
std::format("{:X}", 255); // "FF"
std::format("{:b}", 10); // "1010"
文字列と複数の数値を組み合わせる場合にも便利です。
#include <format>
#include <iostream>
#include <string>
int main()
{
std::string name = "商品A";
int quantity = 5;
double price = 1280.5;
std::string message = std::format(
"{}を{}個、単価{:.2f}円で購入します。",
name,
quantity,
price
);
std::cout << message << '\n';
}
std::formatはC++20の機能です。
使用するには、C++20以降の言語モードと、std::formatに対応した標準ライブラリが必要です。
std::ostringstreamを使う
std::ostringstreamを使うと、ストリーム形式で数値を文字列に変換できます。
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
int main()
{
double value = 3.1415926535;
std::ostringstream stream;
stream << std::fixed
<< std::setprecision(2)
<< value;
std::string text = stream.str();
std::cout << text << '\n';
}
実行結果は次のとおりです。
3.14
よく使われる書式指定には、次のようなものがあります。
std::fixed
std::scientific
std::setprecision(n)
std::setw(n)
std::setfill('0')
std::hex
std::oct
std::dec
整数をゼロ埋めする例は次のとおりです。
#include <iomanip>
#include <iostream>
#include <sstream>
int main()
{
int number = 123;
std::ostringstream stream;
stream << std::setw(8)
<< std::setfill('0')
<< number;
std::cout << stream.str() << '\n';
}
実行結果は次のとおりです。
00000123
std::setprecisionの意味は、std::fixedを指定しているかどうかで変わります。
std::cout << std::setprecision(2) << 123.456;
// 有効数字2桁として出力される
std::cout << std::fixed
<< std::setprecision(2)
<< 123.456;
// 123.46
std::ostringstreamは、std::formatを利用できない環境や、ロケール、既存のストリーム処理、独自のoperator<<を利用したい場合に適しています。
std::to_charsを使う
std::to_charsは、数値を既存の文字バッファへ直接書き込む関数です。
#include <charconv>
#include <iostream>
#include <string>
#include <system_error>
int main()
{
int value = 123;
char buffer[64];
auto result = std::to_chars(
buffer,
buffer + sizeof(buffer),
value
);
if (result.ec == std::errc{})
{
std::string text(buffer, result.ptr);
std::cout << text << '\n';
}
else
{
std::cerr << "変換に失敗しました。\n";
}
}
std::to_charsは結果を既存のバッファへ直接書き込むため、通常は余分な動的メモリ確保を必要としません。
次のような大量変換に向いています。
- ログの生成
- CSVやJSONの生成
- 通信データの作成
- ゲームやリアルタイム処理
- 大量の数値データの変換
ただし、C++規格が具体的な実行速度を保証しているわけではありません。
「高速になるよう設計されており、多くの実装でストリームより効率的」と考えるのが適切です。
浮動小数点数にも使用できます。
#include <charconv>
#include <iostream>
#include <string>
int main()
{
double value = 3.14159;
char buffer[64];
auto result = std::to_chars(
buffer,
buffer + sizeof(buffer),
value,
std::chars_format::fixed,
2
);
if (result.ec == std::errc{})
{
std::string text(buffer, result.ptr);
std::cout << text << '\n';
}
}
古い標準ライブラリでは、浮動小数点版のstd::to_charsへの対応が不完全な場合があります。
文字列から整数へ変換する方法
std::stoiを使う
std::stoiは、std::stringをintへ変換する関数です。
#include <iostream>
#include <string>
int main()
{
std::string text = "123";
int number = std::stoi(text);
std::cout << number << '\n';
}
同様の関数として、次のものがあります。
| 関数 | 変換先 |
|---|---|
std::stoi | int |
std::stol | long |
std::stoll | long long |
std::stoul | unsigned long |
std::stoull | unsigned long long |
std::stof | float |
std::stod | double |
std::stold | long double |
std::stoiの例外処理
std::stoiは変換に失敗した場合、例外を送出します。
主な例外は次の2つです。
std::invalid_argument:変換可能な数値が存在しないstd::out_of_range:変換結果が型の範囲を超えている
#include <iostream>
#include <stdexcept>
#include <string>
int main()
{
std::string text = "abc";
try
{
int number = std::stoi(text);
std::cout << number << '\n';
}
catch (const std::invalid_argument&)
{
std::cerr << "整数として変換できません。\n";
}
catch (const std::out_of_range&)
{
std::cerr << "整数の範囲を超えています。\n";
}
}
ユーザー入力、設定ファイル、CSV、通信データなど、外部から受け取った文字列を変換する場合は、例外処理を省略しない方が安全です。
文字列の一部だけが数値の場合
std::stoiは、先頭から変換できる部分だけを数値として読み取ります。
#include <iostream>
#include <string>
int main()
{
std::string text = "123abc";
int number = std::stoi(text);
std::cout << number << '\n';
}
実行結果は次のとおりです。
123
文字列全体が整数であることを確認するには、変換が終了した位置を取得します。
#include <iostream>
#include <string>
int main()
{
std::string text = "123abc";
std::size_t position = 0;
try
{
int number = std::stoi(text, &position);
if (position != text.size())
{
std::cerr << "末尾に不正な文字があります。\n";
return 1;
}
std::cout << number << '\n';
}
catch (const std::invalid_argument&)
{
std::cerr << "整数として変換できません。\n";
}
catch (const std::out_of_range&)
{
std::cerr << "整数の範囲を超えています。\n";
}
}
"123abc"の場合、positionには3が格納されます。
ただし、position == text.size()を確認するだけでは、先頭空白を禁止できません。
std::stoi(" 123");
std::stoiは先頭空白を読み飛ばすため、空白を含めた文字列全体が処理されたと判断されます。
前後の空白を禁止したい場合は、別途チェックが必要です。
空白と符号の扱い
std::stoiは、先頭の空白、プラス符号、マイナス符号を扱えます。
std::stoi(" 123"); // 123
std::stoi("+123"); // 123
std::stoi("-123"); // -123
入力形式を厳密に制限したい場合は、変換前に空白や符号の有無を確認する必要があります。
2進数・8進数・16進数を変換する
std::stoiの第3引数で基数を指定できます。
#include <iostream>
#include <string>
int main()
{
int decimal = std::stoi("123", nullptr, 10);
int hexadecimal = std::stoi("FF", nullptr, 16);
int octal = std::stoi("17", nullptr, 8);
int binary = std::stoi("1010", nullptr, 2);
std::cout << decimal << '\n';
std::cout << hexadecimal << '\n';
std::cout << octal << '\n';
std::cout << binary << '\n';
}
実行結果は次のとおりです。
123
255
15
10
基数に0を指定すると、接頭辞から基数を自動判定します。
std::stoi("123", nullptr, 0); // 10進数
std::stoi("077", nullptr, 0); // 8進数
std::stoi("0xFF", nullptr, 0); // 16進数
基数を明示的に16にした場合も、0x接頭辞を扱えます。
std::stoi("0xFF", nullptr, 16); // 255
文字列から浮動小数点数へ変換する方法
std::stodを使う
std::stodは、文字列をdoubleへ変換する関数です。
#include <iostream>
#include <string>
int main()
{
std::string text = "3.14159";
double value = std::stod(text);
std::cout << value << '\n';
}
指数表記にも対応しています。
double value1 = std::stod("1.23e4"); // 12300
double value2 = std::stod("1.23e-4"); // 0.000123
std::stodもstd::stoiと同様に、変換できない場合はstd::invalid_argument、範囲外の場合はstd::out_of_rangeを送出します。
浮動小数点数の誤差
多くのコンピューターでは、浮動小数点数を2進数で近似表現します。
そのため、10進数の0.1などを完全に正確に表現できない場合があります。
#include <iomanip>
#include <iostream>
#include <string>
int main()
{
double value = std::stod("0.1");
std::cout << std::setprecision(20)
<< value
<< '\n';
}
一般的な環境では、次のような値が表示されます。
0.10000000000000000555
表示上は0.1でも、内部的には非常に近い別の値として保存されています。
金額など、10進数としての厳密な計算が必要な場合は、最小単位の整数として管理する方法があります。
日本円であれば、円単位の整数として扱えます。
#include <cstdint>
std::int64_t priceInYen = 1280;
米ドルのように補助通貨単位がある場合は、セント単位で保持できます。
#include <cstdint>
std::int64_t priceInCents = 128050;
// 1280.50ドル
整数1が何を表すのかを、変数名やコメントで明確にすることが重要です。
std::from_charsで厳密に変換する
std::from_charsの特徴
C++17以降では、文字列を数値へ変換する方法としてstd::from_charsを利用できます。
主な特徴は次のとおりです。
- 例外を使用しない
- ロケールに依存しない
- 変換対象の範囲を指定できる
- 不要な動的メモリ確保を避けやすい
- 変換終了位置を確認できる
- 大量の数値変換に向いている
基本的な使用例は次のとおりです。
#include <charconv>
#include <iostream>
#include <string>
#include <system_error>
int main()
{
std::string text = "123";
int number = 0;
const char* begin = text.data();
const char* end = text.data() + text.size();
auto result = std::from_chars(
begin,
end,
number
);
if (result.ec == std::errc{})
{
std::cout << number << '\n';
}
else
{
std::cerr << "変換に失敗しました。\n";
}
}
戻り値には、主に次の情報が含まれます。
result.ptr
result.ec
result.ptr:変換が終了した位置result.ec:変換結果を表すエラーコード
文字列全体を確認する
std::from_charsも、変換可能な部分までを読み取ります。
したがって、文字列全体が整数であることを確認するには、result.ptrが末尾を指していることを確認します。
#include <charconv>
#include <optional>
#include <string_view>
#include <system_error>
std::optional<int> parseIntStrict(std::string_view text)
{
if (text.empty())
{
return std::nullopt;
}
int value = 0;
const char* begin = text.data();
const char* end = text.data() + text.size();
const auto result =
std::from_chars(begin, end, value, 10);
if (result.ec != std::errc{})
{
return std::nullopt;
}
if (result.ptr != end)
{
return std::nullopt;
}
return value;
}
使用例は次のとおりです。
parseIntStrict("123"); // 成功
parseIntStrict("-123"); // 成功
parseIntStrict("123abc"); // 失敗
parseIntStrict(""); // 失敗
先頭空白は読み飛ばさない
std::from_charsは、先頭の空白を自動的に読み飛ばしません。
parseIntStrict(" 123"); // 失敗
std::stoiとは異なり、入力範囲の先頭から有効な数値表現が始まっている必要があります。
末尾の空白も、文字列全体を確認する場合は不正な文字として扱われます。
parseIntStrict("123 "); // 失敗
空白を許可する場合は、変換前に明示的に取り除く必要があります。
先頭のプラス符号は受け付けない
整数向けのstd::from_charsは、符号付き型に対するマイナス符号は扱えますが、先頭のプラス符号は受け付けません。
parseIntStrict("-123"); // 成功
parseIntStrict("+123"); // 失敗
一方、std::stoiでは次の変換が成功します。
std::stoi("+123"); // 123
+123を有効な入力として扱いたい場合は、変換前に先頭の+を自分で処理する必要があります。
0x接頭辞は自動処理しない
整数向けのstd::from_charsでは、基数を16に指定しても、0xや0Xの接頭辞は自動的に処理されません。
std::string_view text = "0xFF";
int value = 0;
auto result = std::from_chars(
text.data(),
text.data() + text.size(),
value,
16
);
この場合、最初の0だけが変換され、xの位置で変換が終了します。
文字列全体を16進数として処理したい場合は、事前に接頭辞を取り除きます。
#include <charconv>
#include <optional>
#include <string_view>
#include <system_error>
std::optional<unsigned int> parseHex(
std::string_view text
)
{
if (text.starts_with("0x") ||
text.starts_with("0X"))
{
text.remove_prefix(2);
}
if (text.empty())
{
return std::nullopt;
}
unsigned int value = 0;
const auto result = std::from_chars(
text.data(),
text.data() + text.size(),
value,
16
);
if (result.ec != std::errc{} ||
result.ptr != text.data() + text.size())
{
return std::nullopt;
}
return value;
}
std::stoiとstd::from_charsでは、接頭辞の扱いが異なるため注意が必要です。
エラーコードを確認する
std::from_charsでは、主に次のエラーコードを確認します。
std::errc::invalid_argument
std::errc::result_out_of_range
変換可能な文字がない場合は、std::errc::invalid_argumentになります。
int value = 0;
std::string_view text = "abc";
auto result = std::from_chars(
text.data(),
text.data() + text.size(),
value
);
数値が変換先の型に収まらない場合は、std::errc::result_out_of_rangeになります。
std::string_view text =
"999999999999999999999999999";
エラーの種類を分けて処理する例は次のとおりです。
if (result.ec == std::errc::invalid_argument)
{
std::cerr << "整数として解釈できません。\n";
}
else if (
result.ec == std::errc::result_out_of_range
)
{
std::cerr << "整数の範囲を超えています。\n";
}
else if (result.ptr != end)
{
std::cerr << "末尾に不正な文字があります。\n";
}
出力引数を使用する場合の注意
次のように、変換先を直接出力引数へ渡すコードには注意が必要です。
bool parseInt(
std::string_view text,
int& value
)
{
auto result = std::from_chars(
text.data(),
text.data() + text.size(),
value
);
return result.ec == std::errc{} &&
result.ptr == text.data() + text.size();
}
たとえば、"123abc"を渡した場合、関数の戻り値はfalseでも、valueには123が代入される可能性があります。
int value = 999;
bool success = parseInt("123abc", value);
// successはfalse
// valueは123に変わる可能性がある
失敗時に出力引数を変更したくない場合は、一時変数を使用します。
#include <charconv>
#include <string_view>
#include <system_error>
bool parseInt(
std::string_view text,
int& value
)
{
if (text.empty())
{
return false;
}
int parsedValue = 0;
const char* begin = text.data();
const char* end = text.data() + text.size();
const auto result =
std::from_chars(begin, end, parsedValue);
if (result.ec != std::errc{} ||
result.ptr != end)
{
return false;
}
value = parsedValue;
return true;
}
この実装では、変換が完全に成功した場合だけvalueへ代入します。
ストリームを使って文字列から数値へ変換する
std::istringstreamを使う
std::istringstreamを使用して、文字列から数値を読み取ることもできます。
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::string text = "123";
std::istringstream stream(text);
int number = 0;
if (stream >> number)
{
std::cout << number << '\n';
}
else
{
std::cerr << "変換に失敗しました。\n";
}
}
複数の値を空白区切りで読み取る場合に便利です。
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::string text = "100 3.14 apple";
std::istringstream stream(text);
int count = 0;
double price = 0.0;
std::string name;
if (stream >> count >> price >> name)
{
std::cout << count << '\n';
std::cout << price << '\n';
std::cout << name << '\n';
}
}
文字列全体を検証する
std::istringstreamも、先頭部分だけが数値の場合は変換に成功します。
std::string text = "123abc";
std::istringstream stream(text);
int number = 0;
if (stream >> number)
{
// 123までは読み取れるため成功する
}
文字列全体が整数であることを確認するには、変換後に残りの文字を確認します。
#include <sstream>
#include <string>
bool parseIntWithStream(
const std::string& text,
int& value
)
{
std::istringstream stream(text);
int parsedValue = 0;
if (!(stream >> parsedValue))
{
return false;
}
stream >> std::ws;
if (!stream.eof())
{
return false;
}
value = parsedValue;
return true;
}
この実装では、末尾の空白は許可されます。
parseIntWithStream("123", value); // 成功
parseIntWithStream("123 ", value); // 成功
parseIntWithStream("123abc", value); // 失敗
単純な数値変換だけであれば、std::stoiやstd::from_charsの方が変換の意図を明確に表現できます。
C言語由来の変換関数
atoiを避けた方がよい理由
C言語由来のatoiでも、文字列を整数へ変換できます。
#include <cstdlib>
#include <iostream>
int main()
{
int value = std::atoi("123");
std::cout << value << '\n';
}
しかし、atoiには変換失敗を判別しにくいという問題があります。
std::atoi("0"); // 0
std::atoi("abc"); // 0
正しい入力の"0"と、変換に失敗した"abc"の両方が0になります。
また、変換結果がintの範囲を超えた場合に、安全にエラーを確認する方法もありません。
新しいC++コードでは、通常は次の関数を優先します。
std::stoi
std::from_chars
strtolを使う
C形式の文字列を処理する必要がある場合は、strtolを使用できます。
#include <cerrno>
#include <cstdlib>
#include <iostream>
int main()
{
const char* text = "123";
char* end = nullptr;
errno = 0;
long value = std::strtol(
text,
&end,
10
);
if (text == end)
{
std::cerr << "数字がありません。\n";
}
else if (errno == ERANGE)
{
std::cerr << "数値が範囲外です。\n";
}
else if (*end != '\0')
{
std::cerr
<< "末尾に不正な文字があります。\n";
}
else
{
std::cout << value << '\n';
}
}
strtolはatoiよりも詳細なエラー確認ができます。
ただし、先頭の空白は自動的に読み飛ばされます。
入力形式を厳密に制限する場合は、空白の確認も必要です。
C++コードだけで完結する場合は、std::from_charsの方が扱いやすいことが多いでしょう。
1文字の数字と整数を変換する
数字の文字を整数へ変換する
1文字の数字を整数へ変換する場合は、'0'との差を利用できます。
char character = '7';
int number = character - '0';
結果は次のとおりです。
number == 7
対象が数字であることを確認してから変換します。
#include <cctype>
#include <iostream>
int main()
{
char character = '7';
if (
std::isdigit(
static_cast<unsigned char>(character)
)
)
{
int number = character - '0';
std::cout << number << '\n';
}
}
std::isdigitへ負のcharを直接渡すと、未定義動作になる可能性があります。
そのため、unsigned charへ変換してから渡す方法が安全です。
0から9までの整数を文字へ変換する
0から9までの整数は、次のように1文字へ変換できます。
int number = 7;
char character =
static_cast<char>('0' + number);
この方法が使用できるのは、基本的に0から9までです。
int number = 12;
char character = '0' + number;
このコードで文字列の"12"が作成されるわけではありません。
複数桁の整数は、std::to_stringやstd::to_charsを使用します。
数字だけの文字列か確認する
ASCII数字のみを許可する
文字列がASCIIの0から9だけで構成されているか確認する例です。
#include <algorithm>
#include <string_view>
bool containsOnlyAsciiDigits(
std::string_view text
)
{
if (text.empty())
{
return false;
}
return std::all_of(
text.begin(),
text.end(),
[](char ch)
{
return ch >= '0' &&
ch <= '9';
}
);
}
使用例は次のとおりです。
containsOnlyAsciiDigits("123"); // true
containsOnlyAsciiDigits("-123"); // false
containsOnlyAsciiDigits("12.3"); // false
containsOnlyAsciiDigits(""); // false
ただし、数字だけで構成されていても、変換先の整数型に収まるとは限りません。
999999999999999999999999999999
文字の種類だけでなく、型の範囲まで確認する場合は、最終的にstd::from_charsなどを使用して変換する必要があります。
符号なし整数へ変換する際の注意点
std::stoulはマイナス符号を受け付ける
次のコードでは、負数だから必ず例外になるとは限りません。
std::string text = "-1";
unsigned long value = std::stoul(text);
std::stoulやstd::stoullは先頭のマイナス符号を受け付けます。
その結果、"-1"が符号なし整数の非常に大きな値として返される場合があります。
負数を禁止する場合は、変換前に明示的に確認します。
#include <charconv>
#include <optional>
#include <string_view>
#include <system_error>
std::optional<unsigned int> parseUnsignedInt(
std::string_view text
)
{
if (text.empty() || text.front() == '-')
{
return std::nullopt;
}
unsigned int value = 0;
const auto result = std::from_chars(
text.data(),
text.data() + text.size(),
value
);
if (
result.ec != std::errc{} ||
result.ptr != text.data() + text.size()
)
{
return std::nullopt;
}
return value;
}
この関数では、次の入力は失敗します。
parseUnsignedInt("-1"); // 失敗
parseUnsignedInt("+1"); // 失敗
parseUnsignedInt("1abc"); // 失敗
先頭の+も許可したい場合は、別途処理する必要があります。
std::string_viewを利用する
コピーせずに文字列を受け取る
変換関数の引数にstd::string_viewを使用すると、さまざまな形式の文字列をコピーせずに受け取れます。
std::optional<int> parseIntStrict(
std::string_view text
);
次のいずれも渡せます。
parseIntStrict("123");
std::string text = "456";
parseIntStrict(text);
std::string_view view = "789";
parseIntStrict(view);
std::string_viewは文字列を所有しません。
元の文字列が破棄された後にstd::string_viewを保持すると、無効な参照になる可能性があります。
変換関数内ですぐに使用する場合は問題になりにくいですが、クラスのメンバーなどへ保存する場合は、参照先の寿命に注意が必要です。
整数型の範囲を確認する
numeric_limitsを使う
整数型の最小値と最大値は、std::numeric_limitsで確認できます。
#include <iostream>
#include <limits>
int main()
{
std::cout
<< std::numeric_limits<int>::min()
<< '\n';
std::cout
<< std::numeric_limits<int>::max()
<< '\n';
}
多くの環境ではintは32ビットで、次の範囲になります。
-2147483648 ~ 2147483647
ただし、C++規格上、intが常に32ビットであるとは限りません。
固定幅整数型を使う
幅を明確にしたい場合は、<cstdint>で定義されている固定幅整数型を利用できます。
#include <cstdint>
std::int32_t value32;
std::int64_t value64;
std::uint32_t unsignedValue32;
ただし、std::int32_tなどの正確な幅を持つ型は、実装がその幅の整数型を提供できる場合に定義されます。
一般的なPCやサーバー環境では、通常利用できます。
少なくとも指定ビット数以上の型が必要な場合は、次の型もあります。
std::int_least32_t
std::int_fast32_t
日本語を含む文字列を変換する
数値部分を取り出して変換する
次のような文字列は、そのままstd::stoiへ渡すことはできません。
std::string text = "価格:1230円";
先頭が数字ではないため、変換前に数値部分を取り出します。
#include <iostream>
#include <string>
int main()
{
std::string text = "価格:1230円";
std::size_t start = text.find(':');
std::size_t end = text.find("円");
if (
start != std::string::npos &&
end != std::string::npos &&
start + 1 < end
)
{
std::string numberText =
text.substr(
start + 1,
end - start - 1
);
int price = std::stoi(numberText);
std::cout << price << '\n';
}
}
実際のアプリケーションでは、区切り文字が存在するか、数値部分が空ではないか、変換結果が範囲内かも確認する必要があります。
全角数字はそのまま変換できない
次の全角数字は、ASCIIの数字とは異なる文字です。
123
std::stoiや通常の整数向けstd::from_charsへ、そのまま渡すことはできません。
変換するには、全角数字を半角数字へ正規化する必要があります。
UTF-8のstd::stringでは、全角数字1文字が複数バイトで表現されます。
そのため、次のように1バイトずつ処理しても、全角数字を1文字として扱うことはできません。
for (char ch : text)
{
// UTF-8の全角数字を1文字として処理できない
}
Unicodeの正規化や全角・半角変換を行う場合は、ICUなどのUnicode対応ライブラリを利用するか、使用する文字コードを明確にしたうえで処理する必要があります。
変換方法の選び方
用途別の推奨方法
用途に応じた代表的な選択肢は次のとおりです。
| 用途 | 推奨方法 |
|---|---|
| 整数を簡単に文字列へ変換 | std::to_string |
| 書式を指定して文字列を作成 | std::format |
| ロケールやストリーム書式を使う | std::ostringstream |
| 文字列を簡単に整数へ変換 | std::stoi |
| 文字列を簡単に小数へ変換 | std::stod |
| 厳密かつ例外なしで解析 | std::from_chars |
| 既存バッファへ数値を書き込む | std::to_chars |
| 複数の値を空白区切りで読む | std::istringstream |
| C APIと連携する | strtol、strtod |
atoi | 新規コードでは原則として避ける |
std::stoiとstd::from_charsの違い
std::stoiと整数向けstd::from_charsでは、入力の扱いが異なります。
| 入力 | std::stoi | std::from_chars |
|---|---|---|
"123" | 成功 | 成功 |
" 123" | 成功 | 失敗 |
"+123" | 成功 | 失敗 |
"-123" | 成功 | 符号付き型なら成功 |
"123abc" | 123まで変換 | 123まで変換 |
"0xFF"を基数16で変換 | 接頭辞を処理する | 0xを自動処理しない |
| 範囲外 | 例外 | エラーコード |
| ロケール | C形式の変換規則の影響を受ける | ロケールに依存しない |
入力形式を柔軟に受け付けたい場合はstd::stoiが使いやすく、入力形式を厳密に制御したい場合はstd::from_charsが適しています。
実務で安全に変換するためのポイント
入力仕様を明確にする
外部から受け取った文字列を数値へ変換する場合は、単に変換できるかどうかだけでなく、次の条件を明確にする必要があります。
- 空文字列を許可するか
- 先頭や末尾の空白を許可するか
- プラス符号を許可するか
- マイナス符号を許可するか
- 小数を許可するか
- 指数表記を許可するか
- 2進数や16進数を許可するか
- 接頭辞を許可するか
- 末尾の文字を許可するか
- 変換先の型に収まるか
NaNや無限大を許可するか
簡単な変換に使う方法
単純に整数を文字列へ変換する場合は、次のように記述できます。
std::string text = std::to_string(value);
書式を指定する場合は、std::formatが便利です。
std::string text =
std::format("{:.2f}", value);
簡単に文字列を整数へ変換する場合は、std::stoiを使用できます。
try
{
int value = std::stoi(text);
}
catch (const std::invalid_argument&)
{
// 数値ではない
}
catch (const std::out_of_range&)
{
// 範囲外
}
文字列全体を厳密に検証する場合は、std::from_charsが適しています。
auto result = std::from_chars(
begin,
end,
value
);
大量の数値を効率的に変換する場合は、std::to_charsとstd::from_charsが有力な選択肢です。
数値と文字列の変換では、変換関数そのものを選ぶだけでなく、どのような入力を正しい値として認めるのかを事前に決めることが重要です。
以上、C++の数値と文字列の変換についてでした。
最後までお読みいただき、ありがとうございました。
