C++ 文字列数値変換 解説

2024-08-02

std::stoullとは?

std::stoull は、C++の標準ライブラリに含まれる関数で、文字列 (string) を unsigned long long 型の整数値に変換する際に使用されます。文字列の中に数値表現が含まれており、それを数値として扱いたい場合に非常に便利です。

std::stoullの働き

  • エラー処理
    変換に失敗した場合、例外 (invalid_argumentまたはout_of_range) を投げます。
  • 数値への変換
    見つかった数値部分を、指定された基数 (base) でunsigned long long型の整数値に変換します。
  • 文字列の解析
    与えられた文字列を先頭から順に解析し、数値を表す部分を探します。

std::stoullの構文

unsigned long long stoull(const string& str, size_t* idx = 0, int base = 10);
  • base
    基数 (デフォルトは10進数。2進数から36進数まで指定可能)
  • idx
    変換が成功した場合、数値部分の直後の文字の位置を格納するポインタ (省略可能)
  • str
    変換したい文字列

使用例

#include <iostream>
#include <string>

int main() {
    std::string str = "12345";
    unsigned long long value = std::stoull(str);

    std::cout << "Converted value: " << value << std::endl;

    // 16進数の文字列を10進数に変換
    std::string hex_str = "FF";
    unsigned long long hex_value = std::stoull(hex_str, nullptr, 16);
    std::cout << "Hex value converted to decimal: " << hex_value << std::endl;

    return 0;
}

注意点

  • オーバーフロー
    変換結果がunsigned long long型の範囲を超える場合、out_of_range例外が投げられます。
  • 基数
    baseの指定を間違えると、意図しない結果になることがあります。
  • エラー処理
    変換に失敗した場合、例外が投げられるため、適切な例外処理が必要です。
  • std::stoul
    文字列をunsigned long型の整数に変換
  • std::stoi
    文字列をint型の整数に変換
  • std::stol
    文字列をlong型の整数に変換

std::stoullは、文字列を数値に変換する際に非常に便利な関数です。数値処理を行うプログラムでは、頻繁に利用されます。ただし、エラー処理や基数の指定など、注意すべき点もあります。



std::stoull 関数を使用する際に、様々なエラーやトラブルが発生する可能性があります。ここでは、よくあるエラーとその解決策について詳しく解説します。

invalid_argument 例外

  • 解決策
    • 文字列が数値として正しい形式か確認する
    • 数値以外の文字を取り除くか、適切な位置に数値のみが存在するようにする
    • 基数の値が正しい範囲内であることを確認する
  • 原因
    • 文字列の先頭が数値でない
    • 文字列中に数値以外の文字が含まれている
    • 基数が不正 (2 <= base <= 36)
#include <iostream>
#include <string>

int main() {
    std::string str = "123a45"; // 数値以外の文字 'a' が含まれる
    try {
        unsigned long long value = std::stoull(str);
        std::cout << value << std::endl;
    } catch (const std::invalid_argument& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
}

out_of_range 例外

  • 解決策
    • 変換結果が想定される範囲内に収まるか事前に確認する
    • より大きな数値型 (例えば、__uint128_t) を使用する
    • 変換前に文字列の長さや数値の桁数をチェックする
  • 原因
    • 変換結果が unsigned long long 型の範囲を超えている
#include <iostream>
#include <string>
#include <limits>

int main() {
    std::string str = "18446744073709551616"; // unsigned long long の最大値を超える
    try {
        unsigned long long value = std::stoull(str);
        std::cout << value << std::endl;
    } catch (const std::out_of_range& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
}

基数の指定ミス

  • 解決策
    • 変換する数値の形式に合わせて基数を正しく指定する
    • 文字列の先頭が "0" で始まっている場合は、8進数または16進数として解釈される可能性があることに注意する
  • 原因
    • 基数を間違えて指定している (例えば、16進数を10進数として扱おうとしている)
  • 解決策
    • idx パラメータに適切なポインタを渡し、変換後の文字の位置を取得する
    • idx パラメータが NULL の場合、変換後の文字の位置は取得できない
  • 原因
    • idx パラメータを正しく使用していない
  • 想定外の動作
    変換する文字列のフォーマットや基数を再度確認する。
  • 実行時エラー
    デバッグモードで実行し、デバッガを使用してエラー発生箇所を特定する。
  • コンパイルエラー
    ヘッダーファイル #include <string> をインクルードしているか確認する。
  • プログラムの周辺コード
    他のコードとの関係性によって、エラーの原因が特定できることがあります。
  • 使用しているコンパイラとバージョン
    コンパイラによって挙動が異なる場合があります。
  • 変換しようとしている文字列の例
    問題の再現に役立ちます。
  • 発生している具体的なエラーメッセージ
    エラーメッセージは、問題解決のヒントになります。


基本的な使い方

#include <iostream>
#include <string>

int main() {
    std::string str = "12345";
    unsigned long long value = std::stoull(str);

    std::cout << "Converted value: " << value << std::endl;
    return 0;
}

異なる基数での変換

#include <iostream>
#include <string>

int main() {
    std::string hex_str = "FF";
    unsigned long long hex_value = std::stoull(hex_str, nullptr, 16); // 16進数として変換

    std::cout << "Hex value converted to decimal: " << hex_value << std::endl;
    return 0;
}

変換位置の取得

#include <iostream>
#include <string>

int main() {
    std::string str = "123abc456";
    size_t idx;
    try {
        unsigned long long value = std::stoull(str, &idx);
        std::cout << "Converted value: " << value << std::endl;
        std::cout << "Conversion ended at: " << idx << std::endl;
    } catch (const std::invalid_argument& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    return 0;
}

例外処理

#include <iostream>
#include <string>

int main() {
    std::string str = "abc";
    try {
        unsigned long long value = std::stoull(str);
    } catch (const std::invalid_argument& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    return 0;
}

複雑な文字列の処理

#include <iostream>
#include <string>
#include <regex>

int main() {
    std::string str = "  12345  ";
    // 先頭と末尾の空白を削除
    str.erase(0, str.find_first_not_of(" "));
    str.erase(str.find_last_not_of(" ") + 1);

    // 正規表現を使って数値部分のみ抽出
    std::regex regex("[^0-9]");
    str = std::regex_replace(str, regex, "");

    try {
        unsigned long long value = std::stoull(str);
        std::cout << "Converted value: " << value << std::endl;
    } catch (const std::invalid_argument& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    return 0;
}
  • 複雑な文字列の処理
    空白や非数値文字を含む文字列を処理する方法を示します。
  • 例外処理
    std::invalid_argument 例外をキャッチしてエラー処理を行う方法を示します。
  • 変換位置の取得
    変換が終了した位置を取得する方法を示します。
  • 異なる基数での変換
    16進数など、異なる基数の文字列を数値に変換する方法を示します。
  • 基本的な使い方
    std::stoull の最もシンプルな使い方を示します。
  • パフォーマンスの最適化
    非常に大きな文字列を扱う場合、パフォーマンスを考慮した実装が必要になることがあります。
  • 複数の数値の変換
    複数の数値が含まれる文字列を分割して、それぞれを数値に変換することができます。
  • 数値範囲のチェック
    変換結果が特定の範囲内にあるかを確認することができます。
  • カスタムエラー処理
    独自のエラーメッセージを表示したり、ログを出力したりすることができます。
  • エラー処理
    例外が発生した場合に適切な処理を行う必要があります。
  • オーバーフロー
    変換結果が unsigned long long 型の範囲を超えると、out_of_range 例外が発生します。
  • 基数の指定
    基数を間違えると、意図しない結果になります。


std::stoullは、文字列をunsigned long long型の整数に変換する上で非常に便利な関数ですが、必ずしも唯一の選択肢ではありません。状況によっては、他の方法がより適している場合があります。

Cスタイルの関数

  • デメリット
    C++の例外処理メカニズムが使えないため、エラー処理が少し面倒になることがあります。
  • メリット
    C++よりも古いコードとの互換性がある場合に便利です。
  • strtoull
    C言語の関数で、std::stoullと同様の機能を持ちます。
    char *endptr;
    const char *str = "12345";
    unsigned long long value = strtoull(str, &endptr, 10);
    

stringstream を利用した方法

  • デメリット
    std::stoullに比べて少し冗長になる場合があります。
  • メリット
    より柔軟な入出力処理が可能で、フォーマット指定などもできます。
  • stringstream
    文字列をストリームのように扱うことができるクラスです。
    #include <sstream>
    std::istringstream iss("12345");
    unsigned long long value;
    iss >> value;
    

手動で実装

  • デメリット
    実装が複雑になり、バグが発生しやすくなります。
  • メリット
    アルゴリズムを深く理解できる
  • 各文字を数値に変換
    文字コードを基に、各文字を数値に変換し、10のべき乗を掛け合わせることで、数値に変換できます。

Boostライブラリのlexical_cast

  • デメリット
    Boostライブラリを導入する必要がある
  • メリット
    シンプルで使いやすい
  • lexical_cast
    文字列と数値間の相互変換を行うテンプレート関数です。
    #include <boost/lexical_cast.hpp>
    unsigned long long value = boost::lexical_cast<unsigned long long>("12345");
    
  • ライブラリの利用
    Boostライブラリを利用できる場合は、lexical_castが便利です。
  • パフォーマンス
    大量の変換を行う場合、パフォーマンスが気になる場合は、手動実装やアセンブリ言語による最適化を検討する必要があります。
  • 互換性
    Cスタイルの関数は、C言語との互換性が必要な場合に適しています。
  • 柔軟性
    stringstreamは、より柔軟な入出力処理が必要な場合に適しています。
  • シンプルさ
    std::stoullが最もシンプルで使いやすいです。

一般的には、std::stoullが最も推奨されます。 特に理由がない限り、std::stoullを使用することで、コードの可読性と保守性を高めることができます。

std::stoullの代替方法はいくつか存在しますが、それぞれの方法にはメリットとデメリットがあります。状況に応じて適切な方法を選択することが重要です。

  • ライブラリの利用状況
  • パフォーマンス
  • 必要な機能
  • コードの可読性
  • より柔軟な入出力処理が必要ですか?
  • 特定のライブラリに依存したくないですか?
  • より高速な処理が必要ですか?