Ответ 1
Выбрасывает ли std::stoi
ошибку на входе "abcxyz"
?
Да.
Я думаю, что ваша путаница может возникнуть из-за того, что strtol
никогда не сообщает об ошибке, кроме переполнения. Он может сообщить, что никакого преобразования не было выполнено, но это никогда не упоминается как условие ошибки в стандарте C.
strtol
определяется одинаково всеми тремя стандартами C, и я избавлю вас от скучных деталей, но в основном определяет "последовательность субъектов", которая является подстрокой входной строки, соответствующей фактическому числу. Следующие четыре условия эквивалентны:
- субъектная последовательность имеет ожидаемую форму (на простом английском языке: это число)
- последовательность объектов не пуста
- произошло преобразование
-
*endptr != nptr
(это имеет смысл только тогда, когдаendptr
не является нулевым)
Когда происходит переполнение, считается, что преобразование произошло.
Теперь совершенно ясно, что поскольку "abcxyz"
не содержит числа, последовательность объектов строки "abcxyz"
должна быть пустой, поэтому никакое преобразование не может быть выполнено. Следующая программа C90/C99/C11 подтвердит это экспериментально:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *nptr = "abcxyz", *endptr[1];
strtol(nptr, endptr, 0);
if (*endptr == nptr)
printf("No conversion could be performed.\n");
return 0;
}
Это означает, что любая соответствующая реализация std::stoi
должна бросать invalid_argument
при задании ввода "abcxyz"
без необязательного базового аргумента.
Означает ли это, что std::stoi
имеет удовлетворительную проверку ошибок?
Нет. Человек, с которым вы разговаривали, правилен, когда она говорит, что std::stoi
более снисходительна, чем выполнение полной проверки errno == 0 && end != start && *end=='\0'
после std::strtol
, потому что std::stoi
молча удаляет все символы, начиная с первого нечислового символа в строка.
На самом деле, с моей головы единственный язык, чье родное преобразование ведет себя как std::stoi
, - это Javascript, и даже тогда вам нужно заставить base 10 с помощью parseInt(n, 10)
избежать специального случая шестнадцатеричных чисел:
input | std::atoi std::stoi Javascript full check
===========+=============================================================
hello | 0 error error(NaN) error
0xygen | 0 0 error(NaN) error
0x42 | 0 0 66 error
42x0 | 42 42 42 error
42 | 42 42 42 42
-----------+-------------------------------------------------------------
languages | Perl, Ruby, Javascript Javascript C#, Java,
| PHP, C... (base 10) Python...
Примечание. Существуют также различия между языками в обработке пробельных и избыточных знаков +.
Хорошо, поэтому я хочу, чтобы полная проверка ошибок, что я должен использовать?
Я не знаю никакой встроенной функции, которая делает это, но boost::lexical_cast<int>
будет делать то, что вы хотите. Он особенно строгий, поскольку он даже отвергает окружающие пробелы, в отличие от функции Python int()
. Обратите внимание, что недопустимые символы и переполнения приводят к одному и тому же исключению, boost::bad_lexical_cast
.
#include <boost/lexical_cast.hpp>
int main() {
std::string s = "42";
try {
int n = boost::lexical_cast<int>(s);
std::cout << "n = " << n << std::endl;
} catch (boost::bad_lexical_cast) {
std::cout << "conversion failed" << std::endl;
}
}