/commands/cpp/convert-string-to-int
c++ convert string to int: std::stoi vs std::from_chars
- tool
- C++
- versions
- C++11 (stoi), C++17 (from_chars)
- os
- any
std::stoi(s) converts an std::string to an int and throws std::invalid_argument if there is no number at the start, or std::out_of_range if the value does not fit. It skips leading white space and stops at the first character that is not part of the number, so "12abc" gives 12. std::from_chars (C++17, <charconv>) reports errors through a return value instead of exceptions and is the faster choice for parsers.
stoi and from_chars side by side
The example uses Clang 20 from the ziglang package on PyPI (with libc++), run through uv; cxx stands for it. On your machine, write g++ or clang++:
cxx() { uv run -q --no-project --python 3.13 --with ziglang==0.15.1 python -m ziglang c++ -fno-color-diagnostics "$@"; }
cat > parse.cpp <<'EOF'
#include <charconv>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <string>
#include <system_error>
std::string with_stoi(const std::string& s) {
try {
std::size_t used = 0;
int v = std::stoi(s, &used);
return std::to_string(v) + (used == s.size() ? "" : " (stopped at " + std::to_string(used) + ")");
} catch (const std::invalid_argument&) {
return "invalid_argument";
} catch (const std::out_of_range&) {
return "out_of_range";
}
}
std::string with_from_chars(const std::string& s) {
int v = 0;
auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), v);
if (ec == std::errc::invalid_argument) return "invalid_argument";
if (ec == std::errc::result_out_of_range) return "result_out_of_range";
return std::to_string(v) + (ptr == s.data() + s.size() ? "" : " (trailing text)");
}
int main() {
for (std::string s : {"42", "-17", " 8", "12abc", "abc", "", "3000000000", "+5"}) {
std::cout << std::left << std::setw(13) << ('"' + s + '"')
<< "stoi: " << std::setw(20) << with_stoi(s)
<< "from_chars: " << with_from_chars(s) << '\n';
}
std::cout << "hex ff: " << std::stoi("ff", nullptr, 16) << '\n';
}
EOF
cxx -std=c++17 -Wall -Wextra parse.cpp -o parse && ./parseOutput
"42" stoi: 42 from_chars: 42 "-17" stoi: -17 from_chars: -17 " 8" stoi: 8 from_chars: invalid_argument "12abc" stoi: 12 (stopped at 2) from_chars: 12 (trailing text) "abc" stoi: invalid_argument from_chars: invalid_argument "" stoi: invalid_argument from_chars: invalid_argument "3000000000" stoi: out_of_range from_chars: result_out_of_range "+5" stoi: 5 from_chars: invalid_argument hex ff: 255
stoi accepts leading spaces and a + sign; from_chars accepts neither, by design: it parses exactly the characters you give it. Both stop at trailing text and report out-of-range values, but only the size_t* argument of stoi (or the returned pointer of from_chars) tells you that characters were left over.
Functions
| Function | Header | On error |
|---|---|---|
| std::stoi(s, &pos, base) | <string> | Throws invalid_argument or out_of_range |
| std::stol / stoll / stoul / stoull | <string> | Same, for long, long long and unsigned types |
| std::from_chars(first, last, value, base) | <charconv> | Returns {ptr, errc}; value unchanged on error (C++17) |
| std::istringstream iss(s); iss >> n | <sstream> | Sets failbit; slow, locale-dependent |
| std::atoi(s.c_str()) | <cstdlib> | No error reporting; undefined behaviour on overflow |
Pitfalls
- Uncaught exceptions. An unhandled
std::invalid_argumentterminates the program with a message such asterminate called after throwing an instance of 'std::invalid_argument'(GCC); catch it around user input. - Wrong argument type.
std::stoitakes astd::string(orstd::wstring); passing acharor astd::string_viewfails with no matching function for call to. Usestd::from_charsforstring_viewand character buffers. - Unsigned parsing.
std::stoul("-1")does not throw; it wraps around to the largest value, like C'sstrtoul. - Same pitfalls as C.
stoicallsstrtolinternally; the C page on strtol explains base 0 and leading zeros.
Confirm it worked
Test empty input, letters, trailing text and a value just above INT_MAX (2147483648), and check that each is rejected the way you expect.
FAQ
Why does std::stoi("12abc") return 12 instead of failing?
stoi parses as many characters as it can and ignores the rest. Pass a pointer to a size_t as the second argument and compare it with the string length to reject trailing characters.
Which exceptions does std::stoi throw?
std::invalid_argument when no conversion could be performed, and std::out_of_range when the value does not fit in an int. Both derive from std::logic_error and std::exception.
Is std::from_chars faster than stoi?
Yes, usually. It does not allocate, throw or depend on the locale, which is why it is preferred in parsers and hot loops. It needs C++17 and <charconv>.