- Forum
- Beginners
- How to handle stoi conversion error?
How to handle stoi conversion error?
I am trying to convert a string to an integer using stoi, I have never touched on try catch and don’t know how to handle the exception or error given by stoi. The code below is what I’ve managed to produce but I can’t seem to be able to let the user re-enter once the stoi conversion has failed. Shouldn’t cin>>input
be enough to start the try catch again? Really new to try catch and error handling. I am trying to loop the input for user when stoi conversion fails. Appreciate it if anyone can give me some examples.
|
|
Last edited on
It is a strong temptation to bug the user to fix errors, but as the years have passed I have come stronger and stronger of the opinion that it is the Wrong ThingTM to do.
If the user’s input is incorrect, complain (be specific) and quit.
Good job!
Here is a more complete example for you to boggle over.
|
|
Notice that on line 13 we also throw if the number of slices is invalid. It does not matter what we throw, since we are just trying to use the same response as a non-integer input. In other words, the catch (...)
block catches both the 1 we throw and either of the things that stoi()
throws.
If we wanted to have a different complaint then we would do something other than throwing that 1. (Such as complain and quit.)
Hope this helps.
I can’t seem to be able to let the user re-enter once the stoi conversion has failed.
In addition to @Duthomhas you need a loop like so:
|
|
Last edited on
The problem with
|
|
is that you now have an infinite loop.
Don’t do that.
@Dunthomhas , if so is there a better way to let the user re-enter the input if the conversion has failed?
Ways of dealing with the infinite loop
|
|
— Give it what it wants; enter something that makes
is_ok
true
— CTRL-C
— taskkill /f /IM myprog.exe
— close command window
— power off
— take a hammer to the PC
mrtammy2008 wrote: |
---|
if so is there a better way to let the user re-enter the input if the conversion has failed? |
No. Make the user try again properly.
For many program, that means starting the program over.
For a (much) larger program, where this input is a smaller piece of the program, exit back to the menu/whatever the user used to get to the part where input was desired.
Don’t forget to tell the user what went wrong.
is that you now have an infinite loop.
No, due to line 13.
Ways of dealing with the infinite loop
Actually it depends on the requirements. One way could be:
|
|
Yes, due to line 9.
Why are you wasting your time on this?
’Cause… whatever.
Topic archived. No new replies allowed.
This wrapper effectively removes some of the available functionality from std::stoi()
because its signature is
int stoi(const std::string& str, std::size_t* pos = 0, int base = 10);
Because your wrapper does not allow a pos
or base
argument you cannot use it to give you the number of characters processed (with pos
) nor to convert using a different base. std::stoi()
provides default values for these arguments so you’d only have to provide them if you need the non-default behavior.
Also, you don’t take the std::string
argument by const reference like std::stoi()
— the string argument is probably not too expensive to copy in this context but why not accept the argument the same way std::stoi()
does?
For completeness, I would also implement the overload of std::stoi()
which accepts a std::wstring
(and possibly std::stol()
and std::stoll()
).
I would also avoid the leading underscore, as identifiers with a leading underscore are reserved.
With these suggestions the wrapper would be implemented as
int stoi(const std::string& str, int* p_value, std::size_t* pos = 0, int base = 10) {
// wrapping std::stoi because it may throw an exception
try {
*p_value = std::stoi(str, pos, base);
return 0;
}
catch (const std::invalid_argument& ia) {
//std::cerr << "Invalid argument: " << ia.what() << std::endl;
return -1;
}
catch (const std::out_of_range& oor) {
//std::cerr << "Out of Range error: " << oor.what() << std::endl;
return -2;
}
catch (const std::exception& e)
{
//std::cerr << "Undefined error: " << e.what() << std::endl;
return -3;
}
}
I personally find that a regex is no overkill for this. Checking user input with a regex will have no negative impact on performance whatsoever.
But I think that you are more interested in exception handling. You need to read many many pages of a good C++ book to understand exception handling and its use cases.
And maybe, but I do not know, you want simply to catch the standard exceptions thrown by std::stoi. These are:
- std::invalid_argument
- std::out_of_range
That would be easy with writing
// Standard exceptions for stoi
catch (const std::invalid_argument & e) {
std::cout << e.what() << "n";
}
catch (const std::out_of_range & e) {
std::cout << e.what() << "n";
}
But maybe you want to learn, how to write own exceptions. For your specific requirements. But please note that your specification, e.g. «more than 10 digits» and «no letter» are maybe not quite what you want. With a machine, where int is 32bit bit, the maximum number that can be converted is: 2147483647. Any number bigger and still having only 10 characters, will throw a std::out_of_range
. On the other hand numbers like 123X would be converted by std::stoi
to 123. So, maybe your requirements are not so clear.
Anyway, to show you, how you could use own exceptions, I create 2 customized exceptions classes. And to make life easy, I derived those from std::exception (recommended).
See the below example:
#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>
class StoiException : public std::exception
{
public:
StoiException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override { return message.c_str(); }
protected:
void setMessage(const std::string& msg) { message = msg; }
protected:
std::string message{};
};
class NoNumber : StoiException
{
public:
NoNumber(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> NoNumber: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
class Overflow : StoiException
{
public:
Overflow(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> Overflow: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
int castInput(std::string& input) {
int result{};
// Check, if there is any letter in the input string
if (std::any_of(input.begin(), input.end(), isalpha)) {
// If so, than throw
throw NoNumber(input);
}
// Check, if string has more than 10 characters
if (input.length() > 10) {
// If so, than throw
throw Overflow(input);
}
result = std::stoi(input);
return result;
}
std::istringstream testCin{ R"(123
567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };
int main() {
std::string input{};
// Read all input
while (testCin >> input) {
try {
// Convert
int convert = castInput(input);
// This will only be shown , if there is no exception
std::cout << "nConverted Number is: " << convert << "n";
}
// Catch all exceptions
catch (const NoNumber & e) {
std::cout << e.what() << "n";
}
catch (const Overflow & e) {
std::cout << e.what() << "n";
}
// Standard exceptions for stoi
catch (const std::invalid_argument & e) {
std::cout << e.what() << "n";
}
catch (const std::out_of_range & e) {
std::cout << e.what() << "n";
}
}
return 0;
}
Of course you can handle the std::stoi
‘s exception also in your custom converter function. Then only your owwn exceptions are visible.
Please see:
#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>
class StoiException : public std::exception
{
public:
StoiException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override { return message.c_str(); }
protected:
void setMessage(const std::string& msg) { message = msg; }
protected:
std::string message{};
};
class NoNumber : StoiException
{
public:
NoNumber(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> NoNumber: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
class Overflow : StoiException
{
public:
Overflow(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> Overflow: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
int castInput(std::string& input) {
int result{};
// Check, if there is any letter in the input string
if (std::any_of(input.begin(), input.end(), isalpha)) {
// If so, than throw
throw NoNumber(input);
}
// Check, if string has more than 10 characters
if (input.length() > 10) {
// If so, than throw
throw Overflow(input);
}
try {
result = std::stoi(input);
}
// Standard exceptions for stoi
catch (const std::invalid_argument & e) {
throw NoNumber(input);
}
catch (const std::out_of_range & e) {
throw Overflow(input);
}
return result;
}
std::istringstream testCin{ R"(123
567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };
int main() {
std::string input{};
// Read all input
while (testCin >> input) {
try {
// Convert
int convert = castInput(input);
// This will only be shown , if there is no exception
std::cout << "nConverted Number is: " << convert << "n";
}
// Catch all exceptions
catch (const NoNumber & e) {
std::cout << e.what() << "n";
}
catch (const Overflow & e) {
std::cout << e.what() << "n";
}
}
return 0;
}
But maybe, what you really want to have, is a function, that encapsulates std::stoi
and has an additional return value to show, if it worked or not.
Please note: The last solution will also convert «123X» to 123. That is the difference to the previous versions.
#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>
std::pair<bool, int> castInput(std::string& input) {
bool ok{ false };
int result{};
try {
result = std::stoi(input);
ok = true;
}
// Catch stoi errors
catch (const std::invalid_argument & e) {}
catch (const std::out_of_range & e) {}
return {ok, result};
}
std::istringstream testCin{ R"(123
567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };
int main() {
std::string input{};
// Read all input
while (testCin >> input) {
const auto [ok, convert] = castInput(input);
if (ok)
std::cout << "Converted value: " << convert << "n";
else
std::cout << "String '" << input << "' could not be convertedn";
}
return 0;
}
Defined in header |
||
int stoi( const std::string& str, std::size_t* pos = nullptr, int base = 10 ); |
(1) | (since C++11) |
long stol( const std::string& str, std::size_t* pos = nullptr, int base = 10 ); |
(2) | (since C++11) |
long long stoll( const std::string& str, std::size_t* pos = nullptr, int base = 10 ); |
(3) | (since C++11) |
Interprets a signed integer value in the string str.
Discards any whitespace characters (as identified by calling std::isspace) until the first non-whitespace character is found, then takes as many characters as possible to form a valid base-n (where n=base
) integer number representation and converts them to an integer value. The valid integer value consists of the following parts:
- (optional) plus or minus sign
- (optional) prefix (
0
) indicating octal base (applies only when the base is 8 or 0) - (optional) prefix (
0x
or0X
) indicating hexadecimal base (applies only when the base is 16 or 0) - a sequence of digits
The set of valid values for base is {0,2,3,...,36}.
The set of valid digits for base-2
integers is {0,1},
for base-3
integers is {0,1,2},
and so on. For bases larger than 10
, valid digits include alphabetic characters, starting from Aa
for base-11
integer, to Zz
for base-36
integer. The case of the characters is ignored.
Additional numeric formats may be accepted by the currently installed C locale.
If the value of base
is 0, the numeric base is auto-detected: if the prefix is 0
, the base is octal, if the prefix is 0x
or 0X
, the base is hexadecimal, otherwise the base is decimal.
If the minus sign was part of the input sequence, the numeric value calculated from the sequence of digits is negated as if by unary minus in the result type.
If pos is not a null pointer, then a pointer ptr
— internal to the conversion functions — will receive the address of the first unconverted character in str.c_str(), and the index of that character will be calculated and stored in *pos, giving the number of characters that were processed by the conversion.
[edit] Parameters
str | — | the string to convert |
pos | — | address of an integer to store the number of characters processed |
base | — | the number base |
[edit] Return value
Integer value corresponding to the content of str.
[edit] Exceptions
- std::invalid_argument if no conversion could be performed.
- std::out_of_range if the converted value would fall out of the range of the result type or if the underlying function (
std::strtol
orstd::strtoll
) sets errno to ERANGE.
[edit] Example
#include <iomanip> #include <iostream> #include <stdexcept> #include <string> #include <utility> int main() { const auto data = { "45", "+45", " -45", "3.14159", "31337 with words", "words and 2", "12345678901", }; for (const std::string s : data) { std::size_t pos{}; try { std::cout << "std::stoi('" << s << "'): "; const int i{std::stoi(s, &pos)}; std::cout << i << "; pos: " << pos << 'n'; } catch (std::invalid_argument const& ex) { std::cout << "std::invalid_argument::what(): " << ex.what() << 'n'; } catch (std::out_of_range const& ex) { std::cout << "std::out_of_range::what(): " << ex.what() << 'n'; const long long ll{std::stoll(s, &pos)}; std::cout << "std::stoll('" << s << "'): " << ll << "; pos: " << pos << 'n'; } } std::cout << "nCalling with different radixes:n"; for (const auto& [s, base] : { std::pair<const char*, int> {"11", 2}, {"22", 3}, {"33", 4}, {"77", 8}, {"99", 10}, {"FF", 16}, {"jJ", 20}, {"Zz", 36}, }) { const int i{std::stoi(s, nullptr, base)}; std::cout << "std::stoi('" << s << "', " << base << "): " << i << 'n'; } }
Possible output:
std::stoi('45'): 45; pos: 2 std::stoi('+45'): 45; pos: 3 std::stoi(' -45'): -45; pos: 4 std::stoi('3.14159'): 3; pos: 1 std::stoi('31337 with words'): 31337; pos: 5 std::stoi('words and 2'): std::invalid_argument::what(): stoi std::stoi('12345678901'): std::out_of_range::what(): stoi std::stoll('12345678901'): 12345678901; pos: 11 Calling with different radixes: std::stoi('11', 2): 3 std::stoi('22', 3): 8 std::stoi('33', 4): 15 std::stoi('77', 8): 63 std::stoi('99', 10): 99 std::stoi('FF', 16): 255 std::stoi('jJ', 20): 399 std::stoi('Zz', 36): 1295
[edit] See also
stoulstoull (C++11)(C++11) |
converts a string to an unsigned integer (function) [edit] |
stofstodstold (C++11)(C++11)(C++11) |
converts a string to a floating point value (function) [edit] |
strtolstrtoll (C++11) |
converts a byte string to an integer value (function) [edit] |
strtoulstrtoull (C++11) |
converts a byte string to an unsigned integer value (function) [edit] |
strtoimaxstrtoumax (C++11)(C++11) |
converts a byte string to std::intmax_t or std::uintmax_t (function) [edit] |
from_chars (C++17) |
converts a character sequence to an integer or floating-point value (function) [edit] |
atoiatolatoll (C++11) |
converts a byte string to an integer value (function) [edit] |
to_string (C++11) |
converts an integral or floating point value to string (function) [edit] |
to_wstring (C++11) |
converts an integral or floating point value to wstring (function) [edit] |
karaulov6 23 / 23 / 6 Регистрация: 23.03.2013 Сообщений: 245 |
||||
1 |
||||
07.06.2017, 20:23. Показов 12499. Ответов 26 Метки нет (Все метки)
std::stoi вызывает ошибку если подать неверную строку (не число) Можно ли как-то исправить std::stoi что бы не вылетала ошибка а возвращало 0 ? Добавлено через 4 минуты
вот эти гады вызывают ошибки Добавлено через 26 минут что бы избежать ошибок нужно везде где используется std::stoi добавлять try {} catch {} ?
0 |
7529 / 6394 / 2914 Регистрация: 14.04.2014 Сообщений: 27,855 |
|
07.06.2017, 21:07 |
2 |
что бы избежать ошибок нужно везде где используется std::stoi добавлять try {} catch {} ? Не ошибки, а исключения. Да, нужно.
0 |
23 / 23 / 6 Регистрация: 23.03.2013 Сообщений: 245 |
|
07.06.2017, 22:37 [ТС] |
3 |
жесть )) огромную кучу кода переписать придется, легче заменить stoi на свой))
0 |
What a waste! 1607 / 1299 / 180 Регистрация: 21.04.2012 Сообщений: 2,727 |
|
07.06.2017, 22:57 |
4 |
жесть )) огромную кучу кода переписать придется, легче заменить stoi на свой)) Почему бы тогда не использовать atoi или strtol, если исключения не нужны?
0 |
284 / 232 / 114 Регистрация: 07.09.2016 Сообщений: 584 |
|
08.06.2017, 00:41 |
5 |
а как вы в своей «агрессивной среде» собираетесь отличать вполне корректную строку «0», которая в 0 превратится от «xyz», если это тоже в 0 будет превращаться?
0 |
7529 / 6394 / 2914 Регистрация: 14.04.2014 Сообщений: 27,855 |
|
08.06.2017, 09:15 |
6 |
DU3, мне тоже не понятна эта неоднозначность atoi().
1 |
23 / 23 / 6 Регистрация: 23.03.2013 Сообщений: 245 |
|
08.06.2017, 11:21 [ТС] |
7 |
Я автоматической заменой изменил все atoi( str.c_str() ) на stoi( str ) , а их в коде было много код стал понятнее и без лишнего мусора но вдруг, оказалось если пользователь введет не число, а какую-нибудь левую строку, все падает к чертям )))) да я просто заменил stoi своей функцией и переименовал ща норм стало
0 |
7529 / 6394 / 2914 Регистрация: 14.04.2014 Сообщений: 27,855 |
|
08.06.2017, 12:09 |
8 |
karaulov6, как это решает проблему с некорректным вводом? Типа, ничего не видно и ладно?
0 |
23 / 23 / 6 Регистрация: 23.03.2013 Сообщений: 245 |
|
08.06.2017, 23:27 [ТС] |
9 |
Стало как положено, т.е как в atoi , неверная строка = 0
0 |
Любитель чаепитий 3737 / 1796 / 563 Регистрация: 24.08.2014 Сообщений: 6,015 Записей в блоге: 1 |
|
11.06.2017, 09:30 |
10 |
если у тебя пользователь в 80 местах данные вводит, то есть смысл вообще всю систему ввода в какой-то подмодуль переделать, где при ошибке ввода пользователя заставят вводить снова и снова.
1 |
23 / 23 / 6 Регистрация: 23.03.2013 Сообщений: 245 |
|
11.06.2017, 14:10 [ТС] |
11 |
Я же уже написал что вырезал эти ошибки из stoi и теперь все работает. Хватит поднимать тему зря)
0 |
Любитель чаепитий 3737 / 1796 / 563 Регистрация: 24.08.2014 Сообщений: 6,015 Записей в блоге: 1 |
|
11.06.2017, 14:12 |
12 |
Не по теме: поставил костыль и рад, надеюсь, на работу программистом с таким подходом тебя никогда не возьмут. а тему не зря поднимаю, а чтобы если кто-то найдет эту тему потом, сразу видел, что это костыль и так делать не надо.
2 |
23 / 23 / 6 Регистрация: 23.03.2013 Сообщений: 245 |
|
11.06.2017, 14:27 [ТС] |
13 |
Ну скажите как надо делать если в коде 100 мест с использованием atoi(std::string.c_str()) а их требуется заменить на stoi(std::string) Но stoi вместо того что бы спокойно вернуть 0, убивает программу. Решений проблемы только 2, добавить в 100 мест кода проверку на исключение или же просто заменить stoi своей функцией Я решил проблему вторым вариантом, или есть третий еще более правильный?
0 |
зомбяк 1581 / 1215 / 345 Регистрация: 14.05.2017 Сообщений: 3,939 |
|
11.06.2017, 14:34 |
14 |
Решений проблемы только 2, добавить в 100 мест кода проверку на исключение напиши функцию (возможно шаблонную), в которой бы происходил stoi(std::string) в требуемый тип, и прямо в этой функции определи try/catch . И чтоб по catch возвращала 0. И ещё inline ей пропиши. Компилятор сделает тебе 100 мест проверок совершено бесплатно, ещё и спасибо скажет
0 |
23 / 23 / 6 Регистрация: 23.03.2013 Сообщений: 245 |
|
11.06.2017, 14:46 [ТС] |
15 |
А я написал что-то другое?
0 |
Любитель чаепитий 3737 / 1796 / 563 Регистрация: 24.08.2014 Сообщений: 6,015 Записей в блоге: 1 |
|
11.06.2017, 14:52 |
16 |
или есть третий еще более правильный? см. #12.
0 |
23 / 23 / 6 Регистрация: 23.03.2013 Сообщений: 245 |
|
11.06.2017, 16:24 [ТС] |
17 |
Да что с вами не так, я же написал что исправил ошибку уже, зачем пытаться включать» режим телепата «и что-то еще доказывать? см. #7
0 |
Любитель чаепитий 3737 / 1796 / 563 Регистрация: 24.08.2014 Сообщений: 6,015 Записей в блоге: 1 |
|
11.06.2017, 17:39 |
18 |
исправил ошибку уже вставить костыль != исправить ошибку.
1 |
23 / 23 / 6 Регистрация: 23.03.2013 Сообщений: 245 |
|
11.06.2017, 17:55 [ТС] |
19 |
А где я написал что костыль вставил? Или «режим телепата» помог до этого додуматься?
0 |
1174 / 835 / 359 Регистрация: 26.02.2015 Сообщений: 3,743 |
|
11.06.2017, 18:02 |
20 |
Я решил проблему вторым вариантом, или есть третий еще более правильный? Есть, не давать пользователю ввести некорректные данные. По-моему Вам об этом уже сказали. При вводе пользователем данных делайте проверку того, что он ввёл и если что-то не так, то возвращайтесь к тому месту, где он вводит нечто и продолжайте так до тех пор, пока он не введет правильно. После того, как введенная строка удовлетворяет, можно уже на обработку эти данные передать. ЧЕМ РАНЬШЕ ВЫ ОБНАРУЖИТЕ, ЧТО ПОЛЬЗОВАТЕЛЬСКИЕ ДАННЫЕ НЕВЕРНЫ, ТЕМ ЛУЧШЕ ЖЕ ДЛЯ ВАС. А самый раннее место это определить — в момент их ввода или же сразу после него.
1 |