Какой способ синтаксического анализа строки (заданной как char *) в int в C ++? Надежная и понятная обработка ошибок является плюсом (вместо возврата нуля ).
261
Какой способ синтаксического анализа строки (заданной как char *) в int в C ++? Надежная и понятная обработка ошибок является плюсом (вместо возврата нуля ).
Ответы:
В новом C ++ 11 для этого есть функции: Stoi, Stol, Stoll, Stoul и так далее.
Это вызовет исключение при ошибке преобразования.
Даже у этих новых функций есть та же проблема, что и у Дэна: они с радостью преобразуют строку «11x» в целое число «11».
Подробнее: http://en.cppreference.com/w/cpp/string/basic_string/stol
источник
size_t
не равен длине строки, он останавливается рано. В этом случае он по-прежнему будет возвращать 11, ноpos
будет равен 2 вместо длины строки 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29Что не делать
Вот мой первый совет: не используйте для этого stringstream . Хотя поначалу это может показаться простым в использовании, вы обнаружите, что вам нужно проделать большую дополнительную работу, если вы хотите надежной работы и хорошей обработки ошибок.
Вот подход, который интуитивно кажется, что он должен работать:
Это имеет большую проблему:
str2int(i, "1337h4x0r")
счастливо вернетсяtrue
иi
получит значение1337
. Мы можем обойти эту проблему, убедившись, чтоstringstream
после преобразования не осталось больше символов :Мы исправили одну проблему, но есть еще несколько других проблем.
Что если число в строке не является основанием 10? Мы можем попытаться приспособить другие базы, установив поток в правильный режим (например,
ss << std::hex
) перед попыткой преобразования. Но это означает, что звонящий должен априори знать, на какой основе находится номер - и как звонящий может это знать? Звонящий еще не знает, что это за номер. Они даже не знают , что эточисло! Как можно ожидать, что они знают, что это за база? Мы могли бы просто указать, что все числа, вводимые в наши программы, должны быть основанием 10 и отклонять шестнадцатеричный или восьмеричный ввод как недействительный. Но это не очень гибко или надежно. Простого решения этой проблемы не существует. Вы не можете просто попробовать преобразование один раз для каждой базы, потому что десятичное преобразование всегда будет успешным для восьмеричных чисел (с начальным нулем), и восьмеричное преобразование может быть успешным для некоторых десятичных чисел. Так что теперь вы должны проверить ведущий ноль. Но ждать! Шестнадцатеричные числа также могут начинаться с ведущего нуля (0x ...). Вздох.Даже если вам удастся справиться с вышеуказанными проблемами, существует еще одна более серьезная проблема: что, если вызывающему абоненту необходимо различать неверный ввод (например, «123foo») и число, выходящее за пределы диапазона
int
(например, «4000000000» для 32-битныйint
) Сstringstream
, нет никакого способа сделать это различие. Мы только знаем, было ли преобразование успешным или неудачным. Если это не удается, мы не можем знать, почему это не удалось. Как видите,stringstream
оставляет желать лучшего, если вы хотите надежности и четкой обработки ошибок.Это приводит меня ко второму совету: не используйте Boost
lexical_cast
для этого . Подумайте, что говоритlexical_cast
документация:Какой?? Мы уже видели, что у
stringstream
него плохой уровень контроля, и все же он говорит, чтоstringstream
следует использовать вместо,lexical_cast
если вам нужен «более высокий уровень контроля». Кроме того, поскольку онlexical_cast
является просто оберткойstringstream
, он страдает от тех же проблем, чтоstringstream
и: плохая поддержка множественных числовых баз и плохая обработка ошибок.Лучшее решение
К счастью, кто-то уже решил все вышеперечисленные проблемы. Стандартная библиотека C содержит
strtol
и семейство, которое не имеет ни одной из этих проблем.Довольно просто для чего-то, что обрабатывает все случаи ошибок, а также поддерживает любую числовую базу от 2 до 36. Если
base
ноль (по умолчанию), он попытается преобразовать любую базу. Или вызывающая сторона может предоставить третий аргумент и указать, что преобразование должно выполняться только для конкретной базы. Он надежен и обрабатывает все ошибки с минимальными усилиями.Другие причины предпочитать
strtol
(и семью):Нет абсолютно никаких причин использовать любой другой метод.
источник
strtol
должен быть потокобезопасным. POSIX также требуетerrno
использования локального хранилища потоков. Даже в не POSIX-системах почти во всех реализацияхerrno
многопоточных систем используется локальное хранилище потоков. Последний стандарт C ++ требуетerrno
совместимости с POSIX. В последнем стандарте C также требуетсяerrno
локальное хранилище потоков. Даже в Windows, которая определенно не совместима с POSIX,errno
она поточно-ориентирована и, соответственно, таковаstrtol
.std::stol
соответствующие исключения, а не возвращаемые константы.std::stol
был добавлен в язык C ++. Тем не менее, я не думаю, что будет справедливо сказать, что это «C-кодирование в C ++». Глупо говорить, чтоstd::strtol
это C-кодирование, когда оно явно является частью языка C ++. Мой ответ идеально подходил для C ++, когда он был написан, и он все еще применяется даже с новымstd::stol
. Вызов функций, которые могут генерировать исключения, не всегда является лучшим в любой ситуации программирования.Это более безопасный способ, чем atoi ()
C ++ со стандартной библиотекой stringstream : (спасибо CMS )
С буст библиотеки: (спасибо JK )
Изменить: Исправлена версия stringstream, чтобы он обрабатывал ошибки. (благодаря комментариям CMS и jk к исходному сообщению)
источник
Старый добрый путь до сих пор работает. Я рекомендую strtol или strtoul. Между статусом возврата и endPtr вы можете получить хороший диагностический вывод. Он также хорошо обрабатывает несколько баз.
источник
Вы можете использовать Boost's
lexical_cast
, который оборачивает это в более общий интерфейс.lexical_cast<Target>(Source)
бросаетbad_lexical_cast
на провал.источник
Вы можете использовать поток строк из стандартного библиотеки C ++:
Посмотрите Потоковые ловушки для ловушек обработки ошибок и потоков в C ++.
источник
Вы можете использовать stringstream's
источник
Я думаю, что эти три ссылки подводят итог:
Решения stringstream и lexical_cast примерно такие же, как в лексическом приведении с использованием stringstream.
Некоторые специализации лексического броска используют другой подход, см. Http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp. подробности . Целые числа и числа с плавающей запятой теперь специализированы для преобразования целых чисел в строки.
Можно специализировать lexical_cast для своих нужд и сделать это быстро. Это было бы окончательным решением, удовлетворяющим все стороны, чистым и простым.
В уже упомянутых статьях показано сравнение различных методов преобразования целых <-> строк. Имеют смысл следующие подходы: старый c-way, spirit.karma, fastformat, простой наивный цикл.
Lexical_cast в некоторых случаях подходит, например, для преобразования int в строку.
Преобразование строки в int с использованием лексического приведения не является хорошей идеей, поскольку она в 10-40 раз медленнее, чем atoi, в зависимости от используемой платформы / компилятора.
Boost.Spirit.Karma, кажется, самая быстрая библиотека для преобразования целого числа в строку.
и простой простой цикл из упомянутой выше статьи - это самый быстрый способ преобразования строки в int, очевидно, не самый безопасный, strtol () кажется более безопасным решением
источник
Библиотека C ++ String Toolkit (StrTk) имеет следующее решение:
InputIterator может быть итераторами без знака char *, char * или std :: string, и ожидается, что T будет со знаком int, например со знаком int, int или long
источник
v = (10 * v) + digit;
без необходимости переполняется при вводе строки с текстовым значениемINT_MIN
. Таблица имеет сомнительную ценность по сравнению с простоdigit >= '0' && digit <= '9'
Если у вас есть C ++ 11, соответствующие решения в настоящее время являются C ++ число функций преобразования в
<string>
:stoi
,stol
,stoul
,stoll
,stoull
. Они выдают соответствующие исключения при неправильном вводе и используют быстрые и маленькиеstrto*
функции под капотом.Если вы застряли с более ранней версией C ++, вы бы с легкостью переносили эти функции в своей реализации.
источник
Начиная с C ++ 17 вы можете использовать
std::from_chars
из<charconv>
заголовка как описано здесь .Например:
В качестве бонуса он также может обрабатывать другие базы, например шестнадцатеричные.
источник
Мне нравится ответ Дана Молдинга , я просто добавлю немного стиля C ++:
Он работает как для std :: string, так и для const char * посредством неявного преобразования. Это также полезно для базового преобразования, например, все
to_int("0x7b")
иto_int("0173")
иto_int("01111011", 2)
иto_int("0000007B", 16)
иto_int("11120", 3)
иto_int("3L", 34);
будет возвращать 123.В отличие от
std::stoi
этого работает в pre-C ++ 11. Кроме того, в отличиеstd::stoi
,boost::lexical_cast
иstringstream
он генерирует исключения для странных строк, таких как «123hohoho».NB. Эта функция допускает начальные пробелы, но не конечные пробелы, т.е.
to_int(" 123")
возвращает 123, аto_int("123 ")
выбрасывает исключение. Убедитесь, что это приемлемо для вашего варианта использования или скорректируйте код.Такая функция может быть частью STL ...
источник
Я знаю три способа преобразования String в int:
Либо используйте функцию Stoi (String to int), либо просто используйте Stringstream, третий способ перейти к индивидуальному преобразованию, код приведен ниже:
1-й метод
2-й метод
3-й метод - но не для индивидуального преобразования
источник
Мне нравится ответ Дэна , особенно из-за исключения исключений. Для разработки встраиваемых систем и других низкоуровневых систем может не существовать надлежащей структуры исключений.
Добавлена проверка пробела после правильной строки ... эти три строки
Добавлена проверка на ошибки разбора тоже.
Вот полная функция ..
источник
" "
.strtol()
не указывается для установки,errno
когда преобразование не происходит. Лучше использовать,if (s == end) return INCONVERTIBLE;
чтобы обнаружить отсутствие конверсии. И тогдаif (*s == '\0' || *end != '\0')
можно упростить доif (*end)
2)|| l > LONG_MAX
и|| l < LONG_MIN
бесполезно - они никогда не верны.Вы можете использовать этот определенный метод.
И если бы вам пришлось преобразовать строку в целое число, вы бы просто сделали следующее.
Выход будет 102.
источник
atoi
не похоже на «путь C ++» в свете других ответов, таких как принятыйstd::stoi()
.Я знаю, что это более старый вопрос, но я сталкивался с ним много раз и до сих пор не нашел решения с хорошими шаблонами, имеющего следующие характеристики:
Итак, вот мой, с тестовым ремешком. Поскольку он использует функции C strtoull / strtoll под капотом, он всегда сначала конвертируется в самый большой доступный тип. Затем, если вы не используете самый большой тип, он выполнит дополнительные проверки диапазона, чтобы убедиться, что ваш тип не был переполнен. Для этого он немного менее эффективен, чем если бы вы правильно выбрали strtol / strtoul. Тем не менее, это также работает для шорт / символов и, насколько мне известно, не существует стандартной библиотечной функции, которая делает это тоже.
Наслаждаться; надеюсь, кто-то найдет это полезным.
StringToDecimal
метод пользовательской земли; он перегружен, поэтому его можно вызвать так:или это:
Я ненавижу повторять тип int, поэтому предпочитаю последний. Это гарантирует, что при изменении типа «а» один не получит плохих результатов. Я хотел бы, чтобы компилятор мог понять это так:
... но C ++ не выводит типы возвращаемых шаблонов, так что это лучшее, что я могу получить.
Реализация довольно проста:
CstrtoxllWrapper
оборачивает оба,strtoull
иstrtoll
, вызывая то, что необходимо на основании подписи типа шаблона и предоставляя некоторые дополнительные гарантии (например, отрицательный ввод запрещен, если он не подписан, и он гарантирует, что вся строка была преобразована).CstrtoxllWrapper
используютсяStringToSigned
иStringToUnsigned
с самым большим длиной типа (длинный / без знака долго долго) , доступного для компилятора; это позволяет выполнить максимальное преобразование. Затем, если это необходимо,StringToSigned
/StringToUnsigned
выполняет окончательную проверку диапазона базового типа. Наконец, метод конечной точки,StringToDecimal
решает, какой из шаблонных методов StringTo * вызывать на основе подписи нижележащего типа.Я думаю, что большая часть мусора может быть оптимизирована компилятором; почти все должно быть детерминированным во время компиляции. Любые комментарии по этому аспекту были бы мне интересны!
источник
long long
вместоintmax_t
?if (ePtr != str)
. Кроме того, используйтеisspace((unsigned char) *ePtr)
для правильной обработки отрицательных значений*ePtr
.В C вы можете использовать
int atoi (const char * str)
,Анализирует C-строку str, интерпретируя ее содержимое как целое число, которое возвращается как значение типа int.
источник
atoi
в этом вопросе, я знаю об этом. Вопрос явно не о C, а о C ++. -1