15.6. Parsing numbers with error reporting¶
Converting a string to an integer should distinguish valid input from invalid
input and detect values that cannot fit in the result type. std::stoi
from <string> returns an int and throws std::invalid_argument or
std::out_of_range when it cannot produce one. It also reports how many
characters it consumed, so we can reject a partially parsed value.
Unlike atoi, this interface does not silently make a failed conversion
look like a valid zero. Do not strip every nondigit from a string: that would
turn -12 into 12 or 12x3 into 123 instead of detecting a problem.
For a format that permits comma-separated thousands, first validate the groups, then remove the commas. Phone numbers and identifiers should remain strings; arithmetic conversion loses leading zeros and other meaningful formatting.
1#include <cstddef>
2#include <iostream>
3#include <stdexcept>
4#include <string>
5
6int parse_distance(const std::string& text) {
7 std::string digits;
8 std::size_t group_size = 0;
9 bool has_comma = false;
10 for (char ch : text) {
11 if (ch >= '0' && ch <= '9') {
12 digits += ch;
13 ++group_size;
14 } else if (ch == ',') {
15 if (group_size == 0 || (!has_comma && group_size > 3) ||
16 (has_comma && group_size != 3)) {
17 throw std::invalid_argument("invalid digit grouping");
18 }
19 has_comma = true;
20 group_size = 0;
21 } else {
22 throw std::invalid_argument("expected a nonnegative integer");
23 }
24 }
25 if (digits.empty() || (has_comma && group_size != 3)) {
26 throw std::invalid_argument("incomplete distance");
27 }
28 std::size_t used = 0;
29 int value = std::stoi(digits, &used);
30 if (used != digits.size()) {
31 throw std::invalid_argument("trailing characters");
32 }
33 return value;
34}
35
36int main() {
37 try {
38 std::cout << parse_distance("1,750") << '\n';
39 } catch (const std::exception& error) {
40 std::cerr << error.what() << '\n';
41 return 1;
42 }
43}
The examples 1750 and 1,750 produce the same value. Inputs such as
1,,750, 17,50, -12, an empty string, or letters are rejected.
An integer larger than int can represent is also rejected. The accumulated
digits string is an example of building a result one character at a time.
What does the atoi() function do?
Which of the following strings will return "2020" when passed into convertToInt()?
Return a copy of text with every old_char replaced by new_char.
-
ch = new_char; } } -
return text; } -
for (char& ch : text) { -
if (ch == old_char) { -
std::string replace_with(std::string text, char old_char, char new_char) {