Какой способ синтаксического анализа строки (заданной как char *) в int в C ++? Надежная и понятная обработка ошибок является плюсом (вместо возврата нуля ).
Какой способ синтаксического анализа строки (заданной как char *) в int в C ++? Надежная и понятная обработка ошибок является плюсом (вместо возврата нуля ).
Ответы:
В новом C ++ 11 для этого есть функции: Stoi, Stol, Stoll, Stoul и так далее.
int myNr = std::stoi(myString);
Это вызовет исключение при ошибке преобразования.
Даже у этих новых функций есть та же проблема, что и у Дэна: они с радостью преобразуют строку «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 . Хотя поначалу это может показаться простым в использовании, вы обнаружите, что вам нужно проделать большую дополнительную работу, если вы хотите надежной работы и хорошей обработки ошибок.
Вот подход, который интуитивно кажется, что он должен работать:
bool str2int (int &i, char const *s)
{
std::stringstream ss(s);
ss >> i;
if (ss.fail()) {
// not an integer
return false;
}
return true;
}
Это имеет большую проблему: str2int(i, "1337h4x0r")
счастливо вернется true
и i
получит значение1337
. Мы можем обойти эту проблему, убедившись, что stringstream
после преобразования не осталось больше символов :
bool str2int (int &i, char const *s)
{
char c;
std::stringstream ss(s);
ss >> i;
if (ss.fail() || ss.get(c)) {
// not an integer
return false;
}
return true;
}
Мы исправили одну проблему, но есть еще несколько других проблем.
Что если число в строке не является основанием 10? Мы можем попытаться приспособить другие базы, установив поток в правильный режим (например,ss << std::hex
) перед попыткой преобразования. Но это означает, что звонящий должен априори знать, на какой основе находится номер - и как звонящий может это знать? Звонящий еще не знает, что это за номер. Они даже не знают , что эточисло! Как можно ожидать, что они знают, что это за база? Мы могли бы просто указать, что все числа, вводимые в наши программы, должны быть основанием 10 и отклонять шестнадцатеричный или восьмеричный ввод как недействительный. Но это не очень гибко или надежно. Простого решения этой проблемы не существует. Вы не можете просто попробовать преобразование один раз для каждой базы, потому что десятичное преобразование всегда будет успешным для восьмеричных чисел (с начальным нулем), и восьмеричное преобразование может быть успешным для некоторых десятичных чисел. Так что теперь вы должны проверить ведущий ноль. Но ждать! Шестнадцатеричные числа также могут начинаться с ведущего нуля (0x ...). Вздох.
Даже если вам удастся справиться с вышеуказанными проблемами, существует еще одна более серьезная проблема: что, если вызывающему абоненту необходимо различать неверный ввод (например, «123foo») и число, выходящее за пределы диапазона int
(например, «4000000000» для 32-битный int
) С stringstream
, нет никакого способа сделать это различие. Мы только знаем, было ли преобразование успешным или неудачным. Если это не удается, мы не можем знать, почему это не удалось. Как видите, stringstream
оставляет желать лучшего, если вы хотите надежности и четкой обработки ошибок.
Это приводит меня ко второму совету: не используйте Boost lexical_cast
для этого . Подумайте, что говорит lexical_cast
документация:
Там, где требуется более высокая степень контроля над преобразованиями, std :: stringstream и std :: wstringstream предлагают более подходящий путь. Там, где требуются не потоковые преобразования, lexical_cast является неподходящим инструментом для работы и не предназначен специально для таких сценариев.
Какой?? Мы уже видели, что у stringstream
него плохой уровень контроля, и все же он говорит, что stringstream
следует использовать вместо, lexical_cast
если вам нужен «более высокий уровень контроля». Кроме того, поскольку он lexical_cast
является просто оберткой stringstream
, он страдает от тех же проблем, что stringstream
и: плохая поддержка множественных числовых баз и плохая обработка ошибок.
К счастью, кто-то уже решил все вышеперечисленные проблемы. Стандартная библиотека C содержит strtol
и семейство, которое не имеет ни одной из этих проблем.
enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };
STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
char *end;
long l;
errno = 0;
l = strtol(s, &end, base);
if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
return OVERFLOW;
}
if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
return UNDERFLOW;
}
if (*s == '\0' || *end != '\0') {
return INCONVERTIBLE;
}
i = l;
return SUCCESS;
}
Довольно просто для чего-то, что обрабатывает все случаи ошибок, а также поддерживает любую числовую базу от 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 ()
const char* str = "123";
int i;
if(sscanf(str, "%d", &i) == EOF )
{
/* error */
}
C ++ со стандартной библиотекой stringstream : (спасибо CMS )
int str2int (const string &str) {
stringstream ss(str);
int num;
if((ss >> num).fail())
{
//ERROR
}
return num;
}
С буст библиотеки: (спасибо JK )
#include <boost/lexical_cast.hpp>
#include <string>
try
{
std::string str = "123";
int number = boost::lexical_cast< int >( str );
}
catch( const boost::bad_lexical_cast & )
{
// Error
}
Изменить: Исправлена версия stringstream, чтобы он обрабатывал ошибки. (благодаря комментариям CMS и jk к исходному сообщению)
Старый добрый путь до сих пор работает. Я рекомендую strtol или strtoul. Между статусом возврата и endPtr вы можете получить хороший диагностический вывод. Он также хорошо обрабатывает несколько баз.
Вы можете использовать Boost'slexical_cast
, который оборачивает это в более общий интерфейс.
lexical_cast<Target>(Source)
бросает bad_lexical_cast
на провал.
Вы можете использовать поток строк из стандартного библиотеки C ++:
stringstream ss(str);
int x;
ss >> x;
if(ss) { // <-- error handling
// use x
} else {
// not a number
}
Состояние потока будет установлено как сбойное, если при попытке прочитать целое число встречается нецифровка.
Посмотрите Потоковые ловушки для ловушек обработки ошибок и потоков в C ++.
Вы можете использовать stringstream's
int str2int (const string &str) {
stringstream ss(str);
int num;
ss >> num;
return num;
}
Я думаю, что эти три ссылки подводят итог:
Решения 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, кажется, самая быстрая библиотека для преобразования целого числа в строку.
ex.: generate(ptr_char, int_, integer_number);
и простой простой цикл из упомянутой выше статьи - это самый быстрый способ преобразования строки в int, очевидно, не самый безопасный, strtol () кажется более безопасным решением
int naive_char_2_int(const char *p) {
int x = 0;
bool neg = false;
if (*p == '-') {
neg = true;
++p;
}
while (*p >= '0' && *p <= '9') {
x = (x*10) + (*p - '0');
++p;
}
if (neg) {
x = -x;
}
return x;
}
Библиотека C ++ String Toolkit (StrTk) имеет следующее решение:
static const std::size_t digit_table_symbol_count = 256;
static const unsigned char digit_table[digit_table_symbol_count] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xFF - 0x07
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x08 - 0x0F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10 - 0x17
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x18 - 0x1F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20 - 0x27
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x28 - 0x2F
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38 - 0x3F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40 - 0x47
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x48 - 0x4F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x50 - 0x57
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58 - 0x5F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x60 - 0x67
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x68 - 0x6F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x70 - 0x77
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x78 - 0x7F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x80 - 0x87
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x88 - 0x8F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x90 - 0x97
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x98 - 0x9F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA0 - 0xA7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA8 - 0xAF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB0 - 0xB7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB8 - 0xBF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC0 - 0xC7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC8 - 0xCF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD0 - 0xD7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD8 - 0xDF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE0 - 0xE7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE8 - 0xEF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xF0 - 0xF7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 0xF8 - 0xFF
};
template<typename InputIterator, typename T>
inline bool string_to_signed_type_converter_impl_itr(InputIterator begin, InputIterator end, T& v)
{
if (0 == std::distance(begin,end))
return false;
v = 0;
InputIterator it = begin;
bool negative = false;
if ('+' == *it)
++it;
else if ('-' == *it)
{
++it;
negative = true;
}
if (end == it)
return false;
while(end != it)
{
const T digit = static_cast<T>(digit_table[static_cast<unsigned int>(*it++)]);
if (0xFF == digit)
return false;
v = (10 * v) + digit;
}
if (negative)
v *= -1;
return true;
}
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>
заголовка как описано здесь .
Например:
#include <iostream>
#include <charconv>
#include <array>
int main()
{
char const * str = "42";
int value = 0;
std::from_chars_result result = std::from_chars(std::begin(str), std::end(str), value);
if(result.error == std::errc::invalid_argument)
{
std::cout << "Error, invalid format";
}
else if(result.error == std::errc::result_out_of_range)
{
std::cout << "Error, value too big for int range";
}
else
{
std::cout << "Success: " << result;
}
}
В качестве бонуса он также может обрабатывать другие базы, например шестнадцатеричные.
Мне нравится ответ Дана Молдинга , я просто добавлю немного стиля C ++:
#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>
int to_int(const std::string &s, int base = 0)
{
char *end;
errno = 0;
long result = std::strtol(s.c_str(), &end, base);
if (errno == ERANGE || result > INT_MAX || result < INT_MIN)
throw std::out_of_range("toint: string is out of range");
if (s.length() == 0 || *end != '\0')
throw std::invalid_argument("toint: invalid string");
return result;
}
Он работает как для 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-й метод
std::string s1 = "4533";
std::string s2 = "3.010101";
std::string s3 = "31337 with some string";
int myint1 = std::stoi(s1);
int myint2 = std::stoi(s2);
int myint3 = std::stoi(s3);
std::cout << s1 <<"=" << myint1 << '\n';
std::cout << s2 <<"=" << myint2 << '\n';
std::cout << s3 <<"=" << myint3 << '\n';
2-й метод
#include <string.h>
#include <sstream>
#include <iostream>
#include <cstring>
using namespace std;
int StringToInteger(string NumberAsString)
{
int NumberAsInteger;
stringstream ss;
ss << NumberAsString;
ss >> NumberAsInteger;
return NumberAsInteger;
}
int main()
{
string NumberAsString;
cin >> NumberAsString;
cout << StringToInteger(NumberAsString) << endl;
return 0;
}
3-й метод - но не для индивидуального преобразования
std::string str4 = "453";
int i = 0, in=0; // 453 as on
for ( i = 0; i < str4.length(); i++)
{
in = str4[i];
cout <<in-48 ;
}
Мне нравится ответ Дэна , особенно из-за исключения исключений. Для разработки встраиваемых систем и других низкоуровневых систем может не существовать надлежащей структуры исключений.
Добавлена проверка пробела после правильной строки ... эти три строки
while (isspace(*end)) {
end++;
}
Добавлена проверка на ошибки разбора тоже.
if ((errno != 0) || (s == end)) {
return INCONVERTIBLE;
}
Вот полная функция ..
#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>
enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };
STR2INT_ERROR str2long (long &l, char const *s, int base = 0)
{
char *end = (char *)s;
errno = 0;
l = strtol(s, &end, base);
if ((errno == ERANGE) && (l == LONG_MAX)) {
return OVERFLOW;
}
if ((errno == ERANGE) && (l == LONG_MIN)) {
return UNDERFLOW;
}
if ((errno != 0) || (s == end)) {
return INCONVERTIBLE;
}
while (isspace((unsigned char)*end)) {
end++;
}
if (*s == '\0' || *end != '\0') {
return INCONVERTIBLE;
}
return SUCCESS;
}
" "
. strtol()
не указывается для установки, errno
когда преобразование не происходит. Лучше использовать, if (s == end) return INCONVERTIBLE;
чтобы обнаружить отсутствие конверсии. И тогда if (*s == '\0' || *end != '\0')
можно упростить до if (*end)
2) || l > LONG_MAX
и || l < LONG_MIN
бесполезно - они никогда не верны.
Вы можете использовать этот определенный метод.
#define toInt(x) {atoi(x.c_str())};
И если бы вам пришлось преобразовать строку в целое число, вы бы просто сделали следующее.
int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}
Выход будет 102.
atoi
не похоже на «путь C ++» в свете других ответов, таких как принятый std::stoi()
.
Я знаю, что это более старый вопрос, но я сталкивался с ним много раз и до сих пор не нашел решения с хорошими шаблонами, имеющего следующие характеристики:
Итак, вот мой, с тестовым ремешком. Поскольку он использует функции C strtoull / strtoll под капотом, он всегда сначала конвертируется в самый большой доступный тип. Затем, если вы не используете самый большой тип, он выполнит дополнительные проверки диапазона, чтобы убедиться, что ваш тип не был переполнен. Для этого он немного менее эффективен, чем если бы вы правильно выбрали strtol / strtoul. Тем не менее, это также работает для шорт / символов и, насколько мне известно, не существует стандартной библиотечной функции, которая делает это тоже.
Наслаждаться; надеюсь, кто-то найдет это полезным.
#include <cstdlib>
#include <cerrno>
#include <limits>
#include <stdexcept>
#include <sstream>
static const int DefaultBase = 10;
template<typename T>
static inline T CstrtoxllWrapper(const char *str, int base = DefaultBase)
{
while (isspace(*str)) str++; // remove leading spaces; verify there's data
if (*str == '\0') { throw std::invalid_argument("str; no data"); } // nothing to convert
// NOTE: for some reason strtoull allows a negative sign, we don't; if
// converting to an unsigned then it must always be positive!
if (!std::numeric_limits<T>::is_signed && *str == '-')
{ throw std::invalid_argument("str; negative"); }
// reset errno and call fn (either strtoll or strtoull)
errno = 0;
char *ePtr;
T tmp = std::numeric_limits<T>::is_signed ? strtoll(str, &ePtr, base)
: strtoull(str, &ePtr, base);
// check for any C errors -- note these are range errors on T, which may
// still be out of the range of the actual type we're using; the caller
// may need to perform additional range checks.
if (errno != 0)
{
if (errno == ERANGE) { throw std::range_error("str; out of range"); }
else if (errno == EINVAL) { throw std::invalid_argument("str; EINVAL"); }
else { throw std::invalid_argument("str; unknown errno"); }
}
// verify everything converted -- extraneous spaces are allowed
if (ePtr != NULL)
{
while (isspace(*ePtr)) ePtr++;
if (*ePtr != '\0') { throw std::invalid_argument("str; bad data"); }
}
return tmp;
}
template<typename T>
T StringToSigned(const char *str, int base = DefaultBase)
{
static const long long max = std::numeric_limits<T>::max();
static const long long min = std::numeric_limits<T>::min();
long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type
// final range check -- only needed if not long long type; a smart compiler
// should optimize this whole thing out
if (sizeof(T) == sizeof(tmp)) { return tmp; }
if (tmp < min || tmp > max)
{
std::ostringstream err;
err << "str; value " << tmp << " out of " << sizeof(T) * 8
<< "-bit signed range (";
if (sizeof(T) != 1) err << min << ".." << max;
else err << (int) min << ".." << (int) max; // don't print garbage chars
err << ")";
throw std::range_error(err.str());
}
return tmp;
}
template<typename T>
T StringToUnsigned(const char *str, int base = DefaultBase)
{
static const unsigned long long max = std::numeric_limits<T>::max();
unsigned long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type
// final range check -- only needed if not long long type; a smart compiler
// should optimize this whole thing out
if (sizeof(T) == sizeof(tmp)) { return tmp; }
if (tmp > max)
{
std::ostringstream err;
err << "str; value " << tmp << " out of " << sizeof(T) * 8
<< "-bit unsigned range (0..";
if (sizeof(T) != 1) err << max;
else err << (int) max; // don't print garbage chars
err << ")";
throw std::range_error(err.str());
}
return tmp;
}
template<typename T>
inline T
StringToDecimal(const char *str, int base = DefaultBase)
{
return std::numeric_limits<T>::is_signed ? StringToSigned<T>(str, base)
: StringToUnsigned<T>(str, base);
}
template<typename T>
inline T
StringToDecimal(T &out_convertedVal, const char *str, int base = DefaultBase)
{
return out_convertedVal = StringToDecimal<T>(str, base);
}
/*============================== [ Test Strap ] ==============================*/
#include <inttypes.h>
#include <iostream>
static bool _g_anyFailed = false;
template<typename T>
void TestIt(const char *tName,
const char *s, int base,
bool successExpected = false, T expectedValue = 0)
{
#define FAIL(s) { _g_anyFailed = true; std::cout << s; }
T x;
std::cout << "converting<" << tName << ">b:" << base << " [" << s << "]";
try
{
StringToDecimal<T>(x, s, base);
// get here on success only
if (!successExpected)
{
FAIL(" -- TEST FAILED; SUCCESS NOT EXPECTED!" << std::endl);
}
else
{
std::cout << " -> ";
if (sizeof(T) != 1) std::cout << x;
else std::cout << (int) x; // don't print garbage chars
if (x != expectedValue)
{
FAIL("; FAILED (expected value:" << expectedValue << ")!");
}
std::cout << std::endl;
}
}
catch (std::exception &e)
{
if (successExpected)
{
FAIL( " -- TEST FAILED; EXPECTED SUCCESS!"
<< " (got:" << e.what() << ")" << std::endl);
}
else
{
std::cout << "; expected exception encounterd: [" << e.what() << "]" << std::endl;
}
}
}
#define TEST(t, s, ...) \
TestIt<t>(#t, s, __VA_ARGS__);
int main()
{
std::cout << "============ variable base tests ============" << std::endl;
TEST(int, "-0xF", 0, true, -0xF);
TEST(int, "+0xF", 0, true, 0xF);
TEST(int, "0xF", 0, true, 0xF);
TEST(int, "-010", 0, true, -010);
TEST(int, "+010", 0, true, 010);
TEST(int, "010", 0, true, 010);
TEST(int, "-10", 0, true, -10);
TEST(int, "+10", 0, true, 10);
TEST(int, "10", 0, true, 10);
std::cout << "============ base-10 tests ============" << std::endl;
TEST(int, "-010", 10, true, -10);
TEST(int, "+010", 10, true, 10);
TEST(int, "010", 10, true, 10);
TEST(int, "-10", 10, true, -10);
TEST(int, "+10", 10, true, 10);
TEST(int, "10", 10, true, 10);
TEST(int, "00010", 10, true, 10);
std::cout << "============ base-8 tests ============" << std::endl;
TEST(int, "777", 8, true, 0777);
TEST(int, "-0111 ", 8, true, -0111);
TEST(int, "+0010 ", 8, true, 010);
std::cout << "============ base-16 tests ============" << std::endl;
TEST(int, "DEAD", 16, true, 0xDEAD);
TEST(int, "-BEEF", 16, true, -0xBEEF);
TEST(int, "+C30", 16, true, 0xC30);
std::cout << "============ base-2 tests ============" << std::endl;
TEST(int, "-10011001", 2, true, -153);
TEST(int, "10011001", 2, true, 153);
std::cout << "============ irregular base tests ============" << std::endl;
TEST(int, "Z", 36, true, 35);
TEST(int, "ZZTOP", 36, true, 60457993);
TEST(int, "G", 17, true, 16);
TEST(int, "H", 17);
std::cout << "============ space deliminated tests ============" << std::endl;
TEST(int, "1337 ", 10, true, 1337);
TEST(int, " FEAD", 16, true, 0xFEAD);
TEST(int, " 0711 ", 0, true, 0711);
std::cout << "============ bad data tests ============" << std::endl;
TEST(int, "FEAD", 10);
TEST(int, "1234 asdfklj", 10);
TEST(int, "-0xF", 10);
TEST(int, "+0xF", 10);
TEST(int, "0xF", 10);
TEST(int, "-F", 10);
TEST(int, "+F", 10);
TEST(int, "12.4", 10);
TEST(int, "ABG", 16);
TEST(int, "10011002", 2);
std::cout << "============ int8_t range tests ============" << std::endl;
TEST(int8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
TEST(int8_t, "80", 16);
TEST(int8_t, "-80", 16, true, std::numeric_limits<int8_t>::min());
TEST(int8_t, "-81", 16);
TEST(int8_t, "FF", 16);
TEST(int8_t, "100", 16);
std::cout << "============ uint8_t range tests ============" << std::endl;
TEST(uint8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
TEST(uint8_t, "80", 16, true, std::numeric_limits<int8_t>::max()+1);
TEST(uint8_t, "-80", 16);
TEST(uint8_t, "-81", 16);
TEST(uint8_t, "FF", 16, true, std::numeric_limits<uint8_t>::max());
TEST(uint8_t, "100", 16);
std::cout << "============ int16_t range tests ============" << std::endl;
TEST(int16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
TEST(int16_t, "8000", 16);
TEST(int16_t, "-8000", 16, true, std::numeric_limits<int16_t>::min());
TEST(int16_t, "-8001", 16);
TEST(int16_t, "FFFF", 16);
TEST(int16_t, "10000", 16);
std::cout << "============ uint16_t range tests ============" << std::endl;
TEST(uint16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
TEST(uint16_t, "8000", 16, true, std::numeric_limits<int16_t>::max()+1);
TEST(uint16_t, "-8000", 16);
TEST(uint16_t, "-8001", 16);
TEST(uint16_t, "FFFF", 16, true, std::numeric_limits<uint16_t>::max());
TEST(uint16_t, "10000", 16);
std::cout << "============ int32_t range tests ============" << std::endl;
TEST(int32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
TEST(int32_t, "80000000", 16);
TEST(int32_t, "-80000000", 16, true, std::numeric_limits<int32_t>::min());
TEST(int32_t, "-80000001", 16);
TEST(int32_t, "FFFFFFFF", 16);
TEST(int32_t, "100000000", 16);
std::cout << "============ uint32_t range tests ============" << std::endl;
TEST(uint32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
TEST(uint32_t, "80000000", 16, true, std::numeric_limits<int32_t>::max()+1);
TEST(uint32_t, "-80000000", 16);
TEST(uint32_t, "-80000001", 16);
TEST(uint32_t, "FFFFFFFF", 16, true, std::numeric_limits<uint32_t>::max());
TEST(uint32_t, "100000000", 16);
std::cout << "============ int64_t range tests ============" << std::endl;
TEST(int64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
TEST(int64_t, "8000000000000000", 16);
TEST(int64_t, "-8000000000000000", 16, true, std::numeric_limits<int64_t>::min());
TEST(int64_t, "-8000000000000001", 16);
TEST(int64_t, "FFFFFFFFFFFFFFFF", 16);
TEST(int64_t, "10000000000000000", 16);
std::cout << "============ uint64_t range tests ============" << std::endl;
TEST(uint64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
TEST(uint64_t, "8000000000000000", 16, true, std::numeric_limits<int64_t>::max()+1);
TEST(uint64_t, "-8000000000000000", 16);
TEST(uint64_t, "-8000000000000001", 16);
TEST(uint64_t, "FFFFFFFFFFFFFFFF", 16, true, std::numeric_limits<uint64_t>::max());
TEST(uint64_t, "10000000000000000", 16);
std::cout << std::endl << std::endl
<< (_g_anyFailed ? "!! SOME TESTS FAILED !!" : "ALL TESTS PASSED")
<< std::endl;
return _g_anyFailed;
}
StringToDecimal
метод пользовательской земли; он перегружен, поэтому его можно вызвать так:
int a; a = StringToDecimal<int>("100");
или это:
int a; StringToDecimal(a, "100");
Я ненавижу повторять тип int, поэтому предпочитаю последний. Это гарантирует, что при изменении типа «а» один не получит плохих результатов. Я хотел бы, чтобы компилятор мог понять это так:
int a; a = StringToDecimal("100");
... но 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