Как разбирать строку в int на С++?

Что такое С++-метод для разбора строки (заданной как char *) в int? Надежная и ясная обработка ошибок - это плюс (вместо возврат нуля).

Ответ 1

В новом С++ 11 есть функции для этого: stoi, stol, stoll, stoul и т.д.

int myNr = std::stoi(myString);

Он выдает исключение при ошибке преобразования.

Даже эти новые функции по-прежнему имеют ту же самую проблему, как отметил Дэн: они с радостью преобразуют строку "11x" в целое число "11".

Подробнее: http://en.cppreference.com/w/cpp/string/basic_string/stol

Ответ 2

Что не делать

Вот мой первый совет: не используйте 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 предлагает больше соответствующий путь. где конверсии без потока required, 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 (и семью):

  • Он показывает гораздо лучше производительность выполнения
  • Он вводит меньше затрат времени на компиляцию (остальные вытягивают почти в 20 раз больше SLOC из заголовков).
  • Это приводит к наименьшему размеру кода

Нет абсолютно никакой причины использовать какой-либо другой метод.

Ответ 3

Это более безопасный способ C, чем atoi()

const char* str = "123";
int i;

if(sscanf(str, "%d", &i)  == EOF )
{
   /* error */
}

С++ со стандартной библиотекой stringstream: (спасибо CMS)

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  if((ss >> num).fail())
  { 
      //ERROR 
  }
  return num;
}

С boost библиотека: (спасибо 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 на исходном сообщении)

Ответ 4

Вы можете использовать Boost lexical_cast, который обертывает это в более общий интерфейс. lexical_cast<Target>(Source) выдает bad_lexical_cast при сбое.

Ответ 5

Хороший "старый способ С по-прежнему работает. Я рекомендую strtol или strtoul. Между статусом возврата и" endPtr" вы можете дать хороший диагностический результат. Он также хорошо использует несколько оснований.

Ответ 6

Вы можете использовать строковый поток из стандартного либрана С++:

stringstream ss(str);
int x;
ss >> x;

if(ss) { // <-- error handling
  // use x
} else {
  // not a number
}

Состояние потока будет отключено если встречается непознанная цифра, если пытаясь прочитать целое число.

См. Ловушки потока для ошибок обработки ошибок и потоков на С++.

Ответ 7

Вы можете использовать stringstream

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  ss >> num;
  return num;
}

Ответ 8

С++ String Toolkit Library (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

Ответ 9

Я думаю, что эти три ссылки суммируют это:

stringstream и lexical_cast решения примерно такие же, как лексический листинг использует stringstream.

Некоторые специализации лексического литья используют другой подход, см. http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp. Целые числа и поплавки теперь специализированы для преобразования целых чисел в строковые.

Можно специализировать lexical_cast для своих собственных нужд и делать это быстро. Это было бы окончательным решением, удовлетворяющим все стороны, чистым и простым.

В статьях, упомянутых выше, показано сравнение различных методов преобразования целых чисел ↔ strings. Следующие подходы имеют смысл: старый 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;
}

Ответ 10

Если у вас есть С++ 11, то в настоящее время подходящими решениями являются функции преобразования целочисленного числа С++ в <string>: stoi, stol, stoul, stoll, stoull. Они вызывают соответствующие исключения при некорректном вводе и используют быстрые и маленькие функции strto* под капотом.

Если вы застряли с более ранней версией С++, вам будет проще переносить ваши функции в вашей реализации.

Ответ 11

Мне нравится Dan Molding answer, я просто добавлю к нему немного стиля С++:

#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-С++ 11. Также в отличие от std::stoi, boost::lexical_cast и stringstream он выдает исключения для странных строк типа 123hohoho.

NB: эта функция допускает ведущие пробелы, но не конечные пробелы, т.е. to_int(" 123") возвращает 123, а to_int("123 ") генерирует исключение. Убедитесь, что это приемлемо для вашего варианта использования или отредактируйте код.

Такая функция может быть частью STL...

Ответ 12

Я знаю три способа преобразования 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';

Второй метод

#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 ;

}

Ответ 13

Мне нравится Dan answer, esp из-за исключения исключений. Для разработки встроенных систем и разработки других низкоуровневых систем может отсутствовать надлежащая структура Exception.

Добавлена ​​проверка белого пространства после допустимой строки... эти три строки

    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;
}

Ответ 14

Вы можете использовать этот определенный метод.

#define toInt(x) {atoi(x.c_str())};

И если вам нужно было преобразовать из String в Integer, вы просто выполните следующее.

int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}

Выход будет равен 102.

Ответ 15

Я знаю, что это более старый вопрос, но я сталкивался с ним так много раз и на сегодняшний день до сих пор не нашел красиво шаблонного решения со следующими характеристиками:

  • Может преобразовать любую базу (и определить базовый тип)
  • Обнаруживает ошибочные данные (т.е. гарантирует, что вся цепочка, меньше ведущего/конечного пробела, потребляется путем преобразования)
  • Будет гарантировать, что независимо от типа, преобразованного в, допустим диапазон значения строки.

Итак, вот мой, с тест-ремешком. Поскольку он использует функции C strtoull/strtoll под капотом, он всегда конвертирует сначала в самый большой доступный тип. Затем, если вы не используете самый большой тип, он будет выполнять дополнительные проверки диапазона, чтобы проверить, что ваш тип не был превышен (под). Для этого он немного менее эффективен, чем если бы правильно выбрал strtol/strtoul. Однако он также работает для шорт/символов и, насколько мне известно, не существует стандартной библиотечной функции, которая тоже это делает.

Enjoy; надеюсь, кто-то сочтет это полезным.

#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 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, поэтому предпочтите его. Это гарантирует, что если тип 'a' изменяет один, это не дает плохих результатов. Я хочу, чтобы компилятор мог понять это:

int a; a = StringToDecimal("100");

... но, С++ не выводит типы возвращаемых шаблонов, так что лучшее, что я могу получить.

Реализация довольно проста:

CstrtoxllWrapper обертывает как strtoull, так и strtoll, вызов в зависимости от того, что требуется на основе типа подписей типа шаблона и предоставления некоторых дополнительных гарантий (например, отрицательный ввод запрещен, если он не подписан и гарантирует, что вся строка была преобразована).

CstrtoxllWrapper используется StringToSigned и StringToUnsigned с наибольшим типом (long long/unsigned long long), доступным компилятору; это позволяет выполнить максимальное преобразование. Затем, если это необходимо, StringToSigned/StringToUnsigned выполняет окончательные проверки диапазона для базового типа. Наконец, метод конечной точки StringToDecimal решает, какой из методов шаблона StringTo * вызывается на основе базовой подписки.

Я думаю, что большая часть мусора может быть оптимизирована компилятором; почти все должно быть детерминированным во времени компиляции. Любые комментарии по этому аспекту были бы интересны мне!

Ответ 16

В C вы можете использовать int atoi (const char * str),

Разбирает строку C-str, интерпретирующую ее содержимое как целое число, которое возвращается как значение типа int.