2021年10月19日(火) [長年日記]
■ [c++] std::from_charsで文字列を整数に変換
C++で文字列を数値に変換するには<string>ヘッダのstoi()などが使えるが、C++17からは<charconv>のfrom_chars()もある。後者の方が高速。
from_charsで文字列を整数へ変換するのサンプルをメモしておく。本当はdoubleへ変換したいのだけれど、GCCはバージョン11までdoubleへの変換が未実装みたい。VC++なら対応している。
- 数字の前に空白や「+」があるとエラーになる所がstoiの動作と異なるので注意。空白などを勝手に処理しない所が利点でもある。
- 数字の後ろに非数字があってもエラーにはならない。これはstoiと同じ。非数字があるかどうかは判別可能。
- 戻り値の解釈が分かりにくいのでメモ。
- ptrは非数字が最初に現れる位置を指す。
- ecは数値を得られているならデフォルト値(errc{})、得られていないならinvalid_argumentかresult_out_of_rangeのどちらかになる。
- 下のサンプルのto_i()は末尾に非数字があるケースをエラー扱いしていることに注意。
#include <charconv>
#include <iostream>
#include <sstream>
#include <string_view>
#include <system_error>
int to_i(std::string_view sv)
{
int i = 0;
const auto last = sv.data() + sv.size();
const auto [ptr, ec] = std::from_chars(sv.data(), last, i);
if (ec == std::errc{} && ptr == last) return i;
std::ostringstream oss;
oss << "error: value=" << i << ", suffix='" << std::string{ptr, last} << "'";
if (ec == std::errc{}) {
throw std::runtime_error(oss.str());
} else {
throw std::system_error(std::make_error_code(ec), oss.str());
}
}
void call(std::string_view sv)
{
std::cout << "'" << sv << "'" << " --> ";
try {
const auto i = to_i(sv);
std::cout << "ok: " << i << std::endl;
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
}
int main()
{
call("-0123");
call("0123456789012345678901234567890123456789a");
call("+0123");
call(" 0123");
call("0123a");
call("0123 ");
}
'-0123' --> ok: -123 '0123456789012345678901234567890123456789a' --> error: value=0, suffix='a': Numerical result out of range '+0123' --> error: value=0, suffix='+0123': Invalid argument ' 0123' --> error: value=0, suffix=' 0123': Invalid argument '0123a' --> error: value=123, suffix='a' '0123 ' --> error: value=123, suffix=' '
[ツッコミを入れる]