【C++】文字列を数値に変換する方法!stoiから最新from_charsまで

【C++】文字列を数値に変換する方法!stoiから最新from_charsまで C++

皆さん、こんにちは。リーダーです。

大手電機メーカーの組み込み現場で20年以上、CやC++と格闘してきましたが、最近の若いエンジニアが書くコードを見ていて、つい「おっと、そこは危ないぞ」と声をかけてしまうのが「文字列から数値への変換」処理です。

「とりあえず動けばいいからatoiで」

そんな風に考えていた時期が、私にもありました。
ですが、業務システムや製品組み込みの世界では、その「とりあえず」が予期せぬクラッシュやセキュリティホールを招くんです。

今日は、仕事終わりの一杯を最高に美味しくするために、バグを未然に防ぐC++流の文字列・数値変換術を、基礎から最新のテクニックまでじっくり解説していきます。

【執筆者の簡易プロフィール】
リーダー 執筆者:リーダー
  • 当ブログの統括者兼ライター
  • 45歳男性、既婚、2児の父
  • 組み込みエンジニア
  • 新卒で大手電機メーカーに入社し、開発部門に配属、現在も勤務
  • 主な使用言語はC言語とC++
  • 趣味は毎晩の晩酌

モダンC++のスタンダード「std::stoi」と仲間たち

C++11以降を使っているなら、まず検討すべきなのが std::stoi です。
「stoi」は「String To Integer」の略ですね。

昔ながらの atoi との違いは、std::string を直接受け取れることと、エラー時に「例外」を投げてくれることです。

まずは、最も基本的なint型への変換を見てみましょう。

#include <iostream>
#include <string>

int main() {
std::string str = "12345";

// 文字列をintに変換
int num = std::stoi(str);

std::cout << "変換結果: " << num << std::endl;

return 0;
}

このコードのポイントは、単純に変換するだけでなく、型に合わせて関数が用意されていることです。

  • std::stoi:int型へ
  • std::stol:long型へ
  • std::stoll:long long型へ
  • std::stof:float型へ
  • std::stod:double型へ

現場では、扱う数値の範囲をしっかり把握して、適切な関数を選ぶのがプロの流儀です。

現場で必須!stoiの「例外処理」を疎かにするな

std::stoi を使う最大の理由は、エラー検知ができることです。

例えば、数値ではない文字列や、intの範囲を超えた巨大な数字が入力された場合、atoi は「0」を返して沈黙しますが、stoi は怒ってくれます。

#include <iostream>
#include <string>
#include <stdexcept> // 例外処理に必要

int main() {
std::string str = "abc"; // 数値じゃない!

try {
    int num = std::stoi(str);
    std::cout << num << std::endl;
} catch (const std::invalid_argument& e) {
    // 数値として解釈できない場合
    std::cerr << "エラー: 数値ではない文字列です。" << std::endl;
} catch (const std::out_of_range& e) {
    // intの範囲を超えている場合
    std::cerr << "エラー: 数値が範囲外です。"<< std::endl;
}

return 0;
}

解説します。

std::invalid_argument は、そもそも数字が含まれていない時に発生します。
std::out_of_range は、”999999999999999″のようなintに入り切らない数字の時に発生します。

「例外処理は面倒だ」と思うかもしれませんが、現場で原因不明のフリーズを追いかける苦労に比べれば、この数行を書く手間なんて、晩酌のつまみを作る時間よりも短いものですよ。

柔軟性抜群!std::stringstreamを使う方法

特定の形式(例えば16進数や、空白区切りの複数の数値)を変換したいなら、std::stringstream が便利です。

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

int main() {
std::string hex_str = "FF";
int num;

std::stringstream ss;
ss << std::hex << hex_str; // 16進数として読み込む
ss << num;

std::cout << "10進数での値: " << num << std::endl; // 255が出る

return 0;
}

このコードの動きを説明します。

std::stringstream は、文字列を「ストリーム(データの流れ)」として扱うためのクラスです。
std::hex という操縦子(マニピュレータ)を使うことで、16進数文字列をいとも簡単にintへ変換できます。

ただし、注意点があります。stringstream は高機能な分、処理のオーバーヘッドが大きいです。
組み込みのリアルタイム性が求められるループ内で多用すると、パフォーマンスを落とす原因になります。使い所を見極める。これもエンジニアの腕の見せ所ですね。

C++17の救世主「std::from_chars」による超高速変換

もしあなたが最新の環境(C++17以降)を使っているなら、最も効率的で高速な std::from_chars を知っておくべきです。

これは例外を投げず、動的なメモリ確保も行わないため、パフォーマンスが命の現場では「これ一択」になることもあります。

#include <iostream>
#include <charconv> // C++17の必須ヘッダー
#include <string>
#include <system_error>

int main() {
std::string str = "456";
int num = 0;

// 文字列の先頭アドレスと末尾アドレスを渡す
auto result = std::from_chars(str.data(), str.data() + str.size(), num);

if (result.ec == std::errc()) {
    std::cout << "成功: " << num << std::endl;
} else {
    std::cout << "変換に失敗しました" << std::endl;
}

return 0;
}

解説します。

std::from_chars は、<charconv> ヘッダーに含まれる低レベルな関数です。

特徴は、エラー情報を例外ではなく戻り値の構造体(ec メンバ)で返してくれること。
例外が許されないクリティカルなシステムや、大量のログファイルを解析するような処理では、stoi よりも圧倒的に高速に動作します。

結論:結局どれを使えばいいの?

これだけ種類があると迷いますよね。後輩に教えるなら、私は以下の基準で使い分けるように言っています。

  • 基本は std::stoi:手軽で安全。一般的なアプリ開発ならこれで十分。
  • 書式が複雑なら stringstream:16進数や特殊な整形が必要な時。
  • 速度重視なら std::from_chars:1分間に数万回の変換が必要、あるいは例外を使いたくない時。
  • atoi は使わない:エラーを無視するコードは、現代のプロフェッショナルには相応しくありません。

まとめ|変換一つにもエンジニアの性格が出る

C++での文字列・数値変換、いかがでしたか?

std::stoi は例外処理とセットで使い、安全性を確保する。
stringstream は柔軟だが「重い」ことを知っておく。
C++17以降なら from_chars が最速・最強の選択肢。
「動けばいい」を卒業して、エラーに強いコードを目指す。

変換処理は地味ですが、プログラムの入り口(入力処理)であることが多いです。
入り口がガタガタだと、中身がどれほど美しくても信頼されません。

さて、堅苦しい話はここまで。
私も今から、自宅の冷蔵庫から「液体」を「幸福」に変換する作業に入るとしますよ。

以上、リーダーでした。

コメント

タイトルとURLをコピーしました