C++の数値と文字列の変換について

AI実装検定のご案内

C++では、数値を文字列に変換したり、文字列を数値として読み取ったりする方法が複数用意されています。

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

  • 数値から文字列:std::to_stringstd::formatstd::to_chars
  • 文字列から数値:std::stoi系関数、std::from_chars
  • ストリームを利用した変換:std::ostringstreamstd::istringstream
  • C言語由来の関数:strtolstrtodなど

簡単な変換であればstd::to_stringstd::stoiが使いやすく、厳密な入力検証や大量のデータを処理する場合にはstd::from_charsstd::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::formatstd::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::stringintへ変換する関数です。

#include <iostream>
#include <string>

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

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

同様の関数として、次のものがあります。

関数変換先
std::stoiint
std::stollong
std::stolllong long
std::stoulunsigned long
std::stoullunsigned long long
std::stoffloat
std::stoddouble
std::stoldlong 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::stodstd::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に指定しても、0x0Xの接頭辞は自動的に処理されません。

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::stoistd::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::stoistd::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';
    }
}

strtolatoiよりも詳細なエラー確認ができます。

ただし、先頭の空白は自動的に読み飛ばされます。

入力形式を厳密に制限する場合は、空白の確認も必要です。

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_stringstd::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::stoulstd::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と連携するstrtolstrtod
atoi新規コードでは原則として避ける

std::stoiとstd::from_charsの違い

std::stoiと整数向けstd::from_charsでは、入力の扱いが異なります。

入力std::stoistd::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_charsstd::from_charsが有力な選択肢です。

数値と文字列の変換では、変換関数そのものを選ぶだけでなく、どのような入力を正しい値として認めるのかを事前に決めることが重要です。

以上、C++の数値と文字列の変換についてでした。

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

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