C++で文字列から数値に変換する方法について

AI実装検定のご案内

C++で文字列を数値に変換する方法はいくつかあります。

代表的な方法は、次のとおりです。

  • std::stoistd::stodなどを使う
  • std::from_charsを使う
  • std::stringstreamを使う
  • std::strtolstd::strtodを使う
  • std::atoistd::atofを使う

それぞれ書き方やエラー処理の方法が異なるため、用途に応じて使い分けることが大切です。

簡単に変換したい場合はstd::stoi系、例外を使わず厳密に処理したい場合はstd::from_charsが有力な選択肢です。

目次

std::stoiで文字列を整数に変換する

std::stoiは、std::stringint型へ変換する関数です。

#include <iostream>
#include <string>

int main()
{
    std::string text = "123";
    int number = std::stoi(text);

    std::cout << number << '\n';
}

実行結果は次のとおりです。

123

std::stoiは「string to integer」を意味する関数名です。

std::stoi系の主な関数

整数型への変換には、次の関数があります。

関数変換後の型
std::stoiint
std::stollong
std::stolllong long
std::stoulunsigned long
std::stoullunsigned long long

小数へ変換する場合は、次の関数を使います。

関数変換後の型
std::stoffloat
std::stoddouble
std::stoldlong double

std::stodで文字列を小数に変換する

文字列をdouble型へ変換する場合は、std::stodを使用します。

#include <iostream>
#include <string>

int main()
{
    std::string text = "3.14159";
    double number = std::stod(text);

    std::cout << number << '\n';
}

float型へ変換したい場合はstd::stoflong double型へ変換したい場合はstd::stoldを使います。

std::string text = "12.5";

float value1 = std::stof(text);
double value2 = std::stod(text);
long double value3 = std::stold(text);

std::stoiで変換に失敗した場合の処理

std::stoistd::stodは、文字列を正常に変換できなかった場合に例外を送出します。

主に発生する例外は、次の2種類です。

  • std::invalid_argument
  • std::out_of_range

std::invalid_argument

数値として解釈できる部分がない場合に発生します。

#include <iostream>
#include <string>

int main()
{
    std::string text = "abc";

    try {
        int number = std::stoi(text);
        std::cout << number << '\n';
    }
    catch (const std::invalid_argument&) {
        std::cout << "数値として変換できません\n";
    }
}

std::out_of_range

変換後の値が、対象となる数値型の範囲を超えている場合に発生します。

#include <iostream>
#include <string>

int main()
{
    std::string text = "999999999999999999999999";

    try {
        int number = std::stoi(text);
        std::cout << number << '\n';
    }
    catch (const std::invalid_argument&) {
        std::cout << "数値として解釈できません\n";
    }
    catch (const std::out_of_range&) {
        std::cout << "数値がint型の範囲を超えています\n";
    }
}

ユーザー入力や外部ファイルなど、内容が保証されていない文字列を変換する場合は、例外処理を用意しておく必要があります。

文字列全体が数値か確認する方法

std::stoiは、文字列の先頭から数値として解釈できる部分だけを変換します。

たとえば、次のコードはエラーになりません。

std::string text = "123abc";
int number = std::stoi(text);

この場合、numberには123が格納されます。

文字列全体が数値で構成されているか確認するには、std::stoiの第2引数を使用します。

#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::cout << "変換成功: " << number << '\n';
        }
        else {
            std::cout << "数値以外の文字が含まれています\n";
        }
    }
    catch (const std::invalid_argument&) {
        std::cout << "数値として変換できません\n";
    }
    catch (const std::out_of_range&) {
        std::cout << "数値が範囲を超えています\n";
    }
}

positionには、数値として変換された部分の直後の位置が格納されます。

"123abc"の場合、position3です。

文字列全体の長さは6なので、数値以外の文字が残っていると判断できます。

厳密に整数へ変換する関数

#include <string>

bool parseInt(const std::string& text, int& result)
{
    try {
        std::size_t position = 0;
        int value = std::stoi(text, &position, 10);

        if (position != text.size()) {
            return false;
        }

        result = value;
        return true;
    }
    catch (const std::invalid_argument&) {
        return false;
    }
    catch (const std::out_of_range&) {
        return false;
    }
}

この関数では、文字列全体が10進整数として変換できた場合だけtrueを返します。

std::stoiにおける空白の扱い

std::stoiは、数値の前にある空白文字を読み飛ばします。

std::string text = "   123";
int number = std::stoi(text);

この場合、number123になります。

一方で、末尾の空白は数値の一部として変換されません。

std::string text = "123   ";

第2引数で変換位置を確認すると、数字の直後までしか変換されていないことが分かります。

そのため、末尾の空白を許可したい場合は、次のいずれかの処理が必要です。

  • 変換前に前後の空白を削除する
  • 変換後に残った文字がすべて空白か確認する

入力仕様として空白を許可しない場合は、position == text.size()で厳密に判定して問題ありません。

進数を指定して変換する方法

std::stoiの第3引数では、何進数として解釈するかを指定できます。

std::stoi(文字列, 変換位置, 基数);

たとえば、2進数の文字列を10進数へ変換する場合は、次のように記述します。

#include <iostream>
#include <string>

int main()
{
    std::string text = "1010";
    int number = std::stoi(text, nullptr, 2);

    std::cout << number << '\n';
}

実行結果は10です。

int binary = std::stoi("1010", nullptr, 2);
int octal = std::stoi("17", nullptr, 8);
int decimal = std::stoi("123", nullptr, 10);
int hexadecimal = std::stoi("FF", nullptr, 16);

それぞれの変換結果は、次のようになります。

文字列基数結果
"1010"210
"17"815
"123"10123
"FF"16255

基数に0を指定した場合

基数に0を指定すると、文字列の接頭辞から進数が判定されます。

int value1 = std::stoi("123", nullptr, 0);
int value2 = std::stoi("077", nullptr, 0);
int value3 = std::stoi("0xFF", nullptr, 0);

一般的には、次のように解釈されます。

  • 接頭辞なし:10進数
  • 0から始まる:8進数
  • 0xまたは0Xから始まる:16進数

通常の10進整数を扱う場合は、意図しない8進数変換を避けるため、基数10を明示する方法が安全です。

int number = std::stoi(text, nullptr, 10);

std::from_charsで文字列を整数に変換する

C++17以降では、std::from_charsを使用して文字列を数値へ変換できます。

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{} && result.ptr == end) {
        std::cout << "変換成功: " << number << '\n';
    }
    else {
        std::cout << "変換失敗\n";
    }
}

std::from_charsの戻り値

std::from_charsの戻り値には、主に次の2つの情報が含まれます。

result.ptr
result.ec

result.ptrは、変換が終了した位置を示します。

result.ecは、変換時のエラーコードを示します。

正常に変換できた場合は、次の条件になります。

result.ec == std::errc{}

文字列全体が変換されたか確認するには、次の条件も必要です。

result.ptr == end

厳密に整数へ変換する関数

#include <charconv>
#include <string_view>
#include <system_error>

bool parseInt(std::string_view text, int& result)
{
    if (text.empty()) {
        return false;
    }

    int value = 0;

    const char* begin = text.data();
    const char* end = begin + text.size();

    auto conversion = std::from_chars(
        begin,
        end,
        value,
        10
    );

    if (conversion.ec != std::errc{}) {
        return false;
    }

    if (conversion.ptr != end) {
        return false;
    }

    result = value;
    return true;
}

この関数では、文字列全体が10進整数として正しく変換できた場合だけtrueを返します。

std::from_charsのエラー処理

数値として変換できる文字が先頭にない場合は、次のエラーになります。

std::errc::invalid_argument

変換後の値が対象型の範囲を超えている場合は、次のエラーになります。

std::errc::result_out_of_range

個別に判定する場合は、次のように記述できます。

auto conversion = std::from_chars(begin, end, value);

if (conversion.ec == std::errc::invalid_argument) {
    std::cout << "数値として解釈できません\n";
}
else if (conversion.ec == std::errc::result_out_of_range) {
    std::cout << "数値の範囲を超えています\n";
}
else if (conversion.ptr != end) {
    std::cout << "数値以外の文字が含まれています\n";
}
else {
    std::cout << "変換成功\n";
}

"123abc"のような文字列では、123の変換自体は成功します。

そのため、conversion.ecだけではなく、conversion.ptr == endも確認する必要があります。

std::from_charsにおける空白と符号の扱い

std::from_charsは、std::stoiとは入力形式の扱いが異なります。

先頭の空白を読み飛ばさない

std::stoiは先頭の空白を読み飛ばしますが、std::from_charsは読み飛ばしません。

std::stoi(" 123"); // 成功

一方、std::from_chars" 123"を渡すと、変換に失敗します。

先頭のプラス記号を受け付けない

整数版のstd::from_charsは、符号付き整数に対して先頭の-は扱えますが、先頭の+は受け付けません。

"123"   // 成功
"-123"  // 成功
"+123"  // 失敗

先頭の+を許可したい場合は、呼び出し側で取り除いてから変換する必要があります。

入力例ごとの結果

入力結果
"123"成功
"-123"成功
"+123"失敗
" 123"失敗
"123 "数値部分は変換できるが、末尾が残る
"123abc"数値部分は変換できるが、末尾が残る
""失敗

std::from_charsで進数を指定する

整数版のstd::from_charsでは、基数を指定できます。

#include <charconv>
#include <iostream>
#include <string>
#include <system_error>

int main()
{
    std::string text = "FF";
    int number = 0;

    auto result = std::from_chars(
        text.data(),
        text.data() + text.size(),
        number,
        16
    );

    if (result.ec == std::errc{} &&
        result.ptr == text.data() + text.size()) {
        std::cout << number << '\n';
    }
}

実行結果は255です。

基数には、通常2から36までの値を指定します。

0x接頭辞は自動的に処理されない

std::from_charsは、基数16を指定しても0xまたは0X接頭辞を認識しません。

std::string text = "0xFF";

この文字列をそのまま基数16で変換すると、先頭の0だけが変換され、xFFが残ります。

0xFFの形式を扱いたい場合は、先に接頭辞を取り除く必要があります。

#include <charconv>
#include <optional>
#include <string_view>

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;

    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::string_view::starts_withは、C++20以降で使用できます。

std::from_charsで小数を変換する

浮動小数点数版のstd::from_charsは、C++17で標準化されています。

#include <charconv>
#include <iostream>
#include <string>
#include <system_error>

int main()
{
    std::string text = "3.14";
    double number = 0.0;

    auto result = std::from_chars(
        text.data(),
        text.data() + text.size(),
        number
    );

    if (result.ec == std::errc{} &&
        result.ptr == text.data() + text.size()) {
        std::cout << number << '\n';
    }
    else {
        std::cout << "変換失敗\n";
    }
}

現在の主要な標準ライブラリでは対応が進んでいますが、古いコンパイラや標準ライブラリでは、浮動小数点数版が未対応の場合があります。

古い開発環境を対象にする場合は、コンパイラだけでなく、使用している標準ライブラリの対応状況も確認してください。

std::stringstreamで文字列を数値に変換する

std::stringstreamstd::istringstreamを使うと、文字列をストリームとして扱えます。

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

int main()
{
    std::string text = "123";
    std::istringstream stream(text);

    int number = 0;
    stream >> number;

    if (stream) {
        std::cout << "変換成功: " << number << '\n';
    }
    else {
        std::cout << "変換失敗\n";
    }
}

文字列全体を確認する方法

単に数値を読み取るだけでは、"123abc"123として変換されます。

文字列全体が数値であることを確認するには、余分な文字が残っていないか調べます。

#include <sstream>
#include <string>

bool parseInt(const std::string& text, int& result)
{
    std::istringstream stream(text);

    int value = 0;
    char remaining = '\0';

    if (!(stream >> value)) {
        return false;
    }

    if (stream >> remaining) {
        return false;
    }

    result = value;
    return true;
}

operator>>は通常、前後の空白を読み飛ばします。

そのため、次のような入力も正常に変換できます。

   123

末尾に空白しか残っていない場合も、余分な文字なしとして扱えます。

複数の値を読み取る

std::istringstreamは、空白で区切られた複数の値を読み取る場合にも便利です。

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

int main()
{
    std::string text = "10 20 30";
    std::istringstream stream(text);

    int a = 0;
    int b = 0;
    int c = 0;

    if (stream >> a >> b >> c) {
        std::cout << a << ", "
                  << b << ", "
                  << c << '\n';
    }
}

std::stringstreamの長所

std::stringstreamには、次のような長所があります。

  • 書き方が直感的
  • 複数の値をまとめて読み取れる
  • 書式付き入力に向いている
  • ユーザー定義型にも応用できる
  • 前後の空白を自然に処理できる

std::stringstreamの短所

一方で、次のような短所もあります。

  • 単純な数値変換としては処理が重くなりやすい
  • ロケールの影響を受ける
  • 厳密な変換では追加のチェックが必要
  • 大量データの解析には向かない場合がある

複数の値や複雑な書式を読み取る場合には便利ですが、単純な整数変換だけならstd::from_charsのほうが適していることがあります。

std::strtolで文字列を整数に変換する

std::strtolは、C言語由来の整数変換関数です。

#include <cerrno>
#include <cstdlib>
#include <iostream>
#include <string>

int main()
{
    std::string text = "123";

    char* end = nullptr;
    errno = 0;

    long value = std::strtol(
        text.c_str(),
        &end,
        10
    );

    if (text.c_str() == end) {
        std::cout << "数値として変換できません\n";
    }
    else if (errno == ERANGE) {
        std::cout << "数値の範囲に関するエラーです\n";
    }
    else if (*end != '\0') {
        std::cout << "数値以外の文字が含まれています\n";
    }
    else {
        std::cout << "変換成功: " << value << '\n';
    }
}

endには、変換が終了した位置が格納されます。

何も変換できなかった場合は、次の条件になります。

text.c_str() == end

文字列の途中までしか変換されなかった場合は、次の条件になります。

*end != '\0'

範囲に関するエラーが発生した場合は、errnoERANGEが設定されます。

longからintへ変換する場合

std::strtolの戻り値はlong型です。

最終的にint型として使用する場合は、intの範囲も確認する必要があります。

#include <cerrno>
#include <cstdlib>
#include <limits>
#include <string>

bool parseInt(const std::string& text, int& result)
{
    if (text.empty()) {
        return false;
    }

    char* end = nullptr;
    errno = 0;

    long value = std::strtol(
        text.c_str(),
        &end,
        10
    );

    if (text.c_str() == end) {
        return false;
    }

    if (*end != '\0') {
        return false;
    }

    if (errno == ERANGE) {
        return false;
    }

    if (value < std::numeric_limits<int>::min() ||
        value > std::numeric_limits<int>::max()) {
        return false;
    }

    result = static_cast<int>(value);
    return true;
}

std::strtodで文字列を小数に変換する

文字列をdouble型へ変換する場合は、std::strtodを使用できます。

#include <cerrno>
#include <cstdlib>
#include <iostream>
#include <string>

int main()
{
    std::string text = "12.34";

    char* end = nullptr;
    errno = 0;

    double value = std::strtod(
        text.c_str(),
        &end
    );

    if (text.c_str() == end) {
        std::cout << "数値として変換できません\n";
    }
    else if (errno == ERANGE) {
        std::cout << "数値の表現範囲に関するエラーです\n";
    }
    else if (*end != '\0') {
        std::cout << "余分な文字が含まれています\n";
    }
    else {
        std::cout << value << '\n';
    }
}

関連する関数は、次のとおりです。

関数変換後の型
std::strtoffloat
std::strtoddouble
std::strtoldlong double

浮動小数点数では、大きすぎる値だけでなく、絶対値が小さすぎる場合にもERANGEが設定される可能性があります。

std::atoiを使用する方法

std::atoiでも、文字列を整数へ変換できます。

#include <cstdlib>
#include <iostream>

int main()
{
    const char* text = "123";
    int number = std::atoi(text);

    std::cout << number << '\n';
}

ただし、新しく作成するC++コードでは、原則としておすすめできません。

std::atoiが推奨されない理由

std::atoiは、変換に失敗しても適切なエラー情報を返しません。

int value1 = std::atoi("0");
int value2 = std::atoi("abc");

どちらも0になるため、正常な0と変換失敗を区別できません。

また、変換結果がint型の範囲を超えた場合も、安全にエラーとして検出できません。

同様に、次の関数も厳密な入力チェックには向いていません。

std::atol
std::atoll
std::atof

新規コードでは、次のいずれかを使うほうが安全です。

  • std::from_chars
  • std::stoi
  • std::strtol

符号なし整数へ変換する場合の注意点

符号なし整数へ変換する場合は、負数の扱いに注意が必要です。

std::string text = "-1";
unsigned long value = std::stoul(text);

std::stoulは、先頭に負号があるからといって、必ず変換に失敗するわけではありません。

負数を許可しない仕様であれば、変換前に-を明示的に拒否する必要があります。

#include <limits>
#include <string>

bool parseUnsignedInt(
    const std::string& text,
    unsigned int& result
)
{
    if (text.empty() || text.front() == '-') {
        return false;
    }

    try {
        std::size_t position = 0;

        unsigned long value =
            std::stoul(text, &position, 10);

        if (position != text.size()) {
            return false;
        }

        if (value >
            std::numeric_limits<unsigned int>::max()) {
            return false;
        }

        result = static_cast<unsigned int>(value);
        return true;
    }
    catch (const std::invalid_argument&) {
        return false;
    }
    catch (const std::out_of_range&) {
        return false;
    }
}

この関数は、先頭の空白を許可しないことを前提としています。

空白を許可する場合は、先に前後の空白を取り除いてから負号を確認してください。

std::string_viewから数値へ変換する

std::stoi系の関数は、基本的にstd::stringを受け取ります。

そのため、std::string_viewをそのまま渡すことはできません。

std::string_view text = "123";

std::stoiを使う場合は、std::stringへ変換します。

int number = std::stoi(std::string(text));

ただし、この方法では一時的なstd::stringが生成されます。

std::from_charsであれば、std::string_viewのデータを直接渡せます。

#include <charconv>
#include <string_view>
#include <system_error>

bool parseInt(std::string_view text, int& result)
{
    if (text.empty()) {
        return false;
    }

    int value = 0;

    auto conversion = std::from_chars(
        text.data(),
        text.data() + text.size(),
        value,
        10
    );

    if (conversion.ec != std::errc{} ||
        conversion.ptr !=
            text.data() + text.size()) {
        return false;
    }

    result = value;
    return true;
}

文字列のコピーを避けられるため、std::string_viewstd::from_charsは相性のよい組み合わせです。

std::optionalで変換結果を返す

変換成功時は数値、失敗時は値なしを返す設計もできます。

#include <charconv>
#include <optional>
#include <string_view>
#include <system_error>

std::optional<int> parseInt(std::string_view text)
{
    if (text.empty()) {
        return std::nullopt;
    }

    int value = 0;

    const char* begin = text.data();
    const char* end = begin + text.size();

    auto conversion = std::from_chars(
        begin,
        end,
        value,
        10
    );

    if (conversion.ec != std::errc{}) {
        return std::nullopt;
    }

    if (conversion.ptr != end) {
        return std::nullopt;
    }

    return value;
}

使用例は、次のとおりです。

#include <iostream>

int main()
{
    auto result = parseInt("123");

    if (result.has_value()) {
        std::cout << "変換成功: "
                  << *result
                  << '\n';
    }
    else {
        std::cout << "変換失敗\n";
    }
}

より短く書くこともできます。

if (auto value = parseInt("123")) {
    std::cout << *value << '\n';
}

std::optionalを使うと、出力引数を使用せず、成功と失敗を分かりやすく表現できます。

ただし、変換失敗の理由までは区別できません。

失敗理由を詳しく返したい場合は、独自の結果型やC++23のstd::expectedを検討できます。

複数の整数型に対応するテンプレート関数

std::from_charsを使用すると、複数の整数型に対応する汎用関数を作れます。

#include <charconv>
#include <optional>
#include <string_view>
#include <type_traits>

template <typename T>
std::optional<T> parseInteger(
    std::string_view text,
    int base = 10
)
{
    static_assert(
        std::is_integral_v<T> &&
        !std::is_same_v<
            std::remove_cv_t<T>,
            bool
        >,
        "Tにはbool以外の整数型を指定してください"
    );

    if (text.empty()) {
        return std::nullopt;
    }

    if (base < 2 || base > 36) {
        return std::nullopt;
    }

    T value{};

    const char* begin = text.data();
    const char* end = begin + text.size();

    auto conversion = std::from_chars(
        begin,
        end,
        value,
        base
    );

    if (conversion.ec != std::errc{}) {
        return std::nullopt;
    }

    if (conversion.ptr != end) {
        return std::nullopt;
    }

    return value;
}

使用例は、次のとおりです。

auto value1 = parseInteger<int>("123");
auto value2 =
    parseInteger<long long>("9999999999");
auto value3 =
    parseInteger<unsigned int>("FF", 16);

boolも整数型として判定されるため、テンプレートでは明示的に除外しています。

また、基数は2から36までに制限しています。

標準入力から整数を読み取る例

ユーザー入力を一度文字列として受け取り、数値へ変換する方法です。

#include <charconv>
#include <iostream>
#include <optional>
#include <string>
#include <string_view>
#include <system_error>

std::optional<int> parseInt(std::string_view text)
{
    if (text.empty()) {
        return std::nullopt;
    }

    int value = 0;

    const char* begin = text.data();
    const char* end = begin + text.size();

    auto conversion = std::from_chars(
        begin,
        end,
        value,
        10
    );

    if (conversion.ec != std::errc{} ||
        conversion.ptr != end) {
        return std::nullopt;
    }

    return value;
}

int main()
{
    std::cout << "整数を入力してください: ";

    std::string input;
    std::getline(std::cin, input);

    if (auto value = parseInt(input)) {
        std::cout << "入力された整数: "
                  << *value
                  << '\n';
    }
    else {
        std::cout
            << "正しい整数を入力してください\n";
    }
}

この関数は、前後の空白を許可しません。

123

は成功しますが、次の入力は失敗します。

 123

人が入力する画面で前後の空白を許可したい場合は、変換前にトリミング処理を行ってください。

よくある間違い

std::stoiを例外処理なしで使う

int value = std::stoi(userInput);

入力内容が保証されていない場合、変換失敗によってプログラムが終了する可能性があります。

外部入力を処理する場合は、例外処理を用意します。

try {
    int value = std::stoi(userInput);
}
catch (const std::invalid_argument&) {
    // 数値として解釈できない
}
catch (const std::out_of_range&) {
    // int型の範囲外
}

失敗理由を区別しない場合は、std::exceptionでまとめて処理することもできます。

文字列の一部分だけ変換して成功と判断する

int value = std::stoi("123abc");

このコードでは、123への変換に成功します。

文字列全体を確認する場合は、第2引数の位置を調べます。

std::size_t position = 0;
int value = std::stoi(text, &position);

if (position != text.size()) {
    // 余分な文字が残っている
}

std::from_charsecだけ確認する

auto result =
    std::from_chars(begin, end, value);

if (result.ec == std::errc{}) {
    // 成功と判断
}

この判定だけでは、"123abc"も成功になります。

文字列全体を変換できたか確認するため、ptrも調べます。

if (result.ec == std::errc{} &&
    result.ptr == end) {
    // 文字列全体の変換に成功
}

std::atoiでエラー判定する

int value = std::atoi(text.c_str());

if (value == 0) {
    // 変換失敗とは限らない
}

"0"も正常に0へ変換されるため、変換失敗と区別できません。

新規コードでは、std::atoiを使わないほうが安全です。

std::isdigitだけで数値か判定する

for (char c : text) {
    if (!std::isdigit(c)) {
        return false;
    }
}

この方法では、次のような問題を処理できません。

  • 負数
  • 先頭のプラス記号
  • 空文字列
  • 数値型の範囲外
  • 10進数以外の入力
  • 小数や指数表記

文字の形式を確認するだけでなく、最終的には実際の数値変換関数を使用して範囲まで検証する必要があります。

std::isdigitに負のcharを渡す

std::isdigitcharを直接渡すと、環境によっては問題が発生する可能性があります。

使用する場合は、unsigned charへ変換します。

unsigned char c =
    static_cast<unsigned char>(text[i]);

if (std::isdigit(c)) {
    // 数字
}

文字列から数値へ変換する方法の比較

方法エラー通知主な特徴適した用途
std::stoi例外書き方が簡単少量の変換
std::from_charsエラーコード低オーバーヘッドで厳密大量データや厳密な解析
std::stringstreamストリーム状態複数の値や複雑な書式に強い書式付き入力
std::strtolerrnoと終了位置C言語との互換性が高いC APIとの連携
std::atoi実用的な通知なしエラー処理が弱い新規コードでは非推奨

実際の処理速度は、コンパイラや標準ライブラリ、入力内容、最適化設定などによって変わります。

std::from_charsは低オーバーヘッドな変換を目的とした設計ですが、すべての環境で必ず最速になるとは限りません。

性能が重要な場合は、実際の利用環境でベンチマークを行う必要があります。

std::stoistd::from_charsの使い分け

手軽に変換したい場合

簡潔なコードを優先する場合は、std::stoistd::stodが便利です。

int value = std::stoi(text);
double price = std::stod(priceText);

変換回数が少なく、例外処理を利用できるプログラムに適しています。

例外を使わず厳密に変換したい場合

厳密な入力チェックを行いたい場合は、std::from_charsが適しています。

auto result =
    std::from_chars(begin, end, value);

ログ解析、CSV、通信データ、ゲーム、リアルタイム処理など、大量の数値変換が必要な場面でも有力です。

複数の値や複雑な書式を扱う場合

複数の値をまとめて読み取る場合は、std::istringstreamが便利です。

std::istringstream stream(
    "100 3.14 sample"
);

int count;
double rate;
std::string name;

stream >> count >> rate >> name;

C言語のAPIと連携する場合

C言語の関数や既存のCコードと連携する場合は、std::strtolstd::strtodが選択肢になります。

おすすめの実装

C++17以降で、前後の空白を許可せず、10進整数を厳密に変換するなら、次の実装が使いやすいです。

#include <charconv>
#include <optional>
#include <string_view>
#include <system_error>

std::optional<int> parseInt(std::string_view text)
{
    if (text.empty()) {
        return std::nullopt;
    }

    int value = 0;

    const char* begin = text.data();
    const char* end = begin + text.size();

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

この関数の結果は、次のようになります。

parseInt("123");    // 成功
parseInt("-123");   // 成功
parseInt("+123");   // 失敗
parseInt("12abc");  // 失敗
parseInt(" 123");   // 失敗
parseInt("123 ");   // 失敗
parseInt("");       // 失敗

空白や先頭のプラス記号を許可したい場合は、変換前に入力文字列を整形してください。

まとめ

C++で文字列を数値へ変換する方法は、用途によって使い分ける必要があります。

簡単に変換したい場合は、次の方法が便利です。

int value = std::stoi(text);
double number = std::stod(text);

例外を使わず、文字列全体を厳密に確認したい場合は、std::from_charsが適しています。

auto result =
    std::from_chars(begin, end, value);

複数の値や複雑な書式を読み取る場合は、std::istringstreamが便利です。

std::istringstream stream(text);
stream >> value;

C言語との互換性が必要な場合は、std::strtolstd::strtodを使用できます。

一方、std::atoi系は変換失敗や範囲外を安全に判定できないため、新規のC++コードでは原則として避けたほうがよいでしょう。

C++17以降で整数を厳密に変換する場合は、std::string_viewstd::from_charsstd::optionalを組み合わせた実装が、扱いやすく安全な方法です。

以上、C++で文字列から数値に変換する方法についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次