Jak przetworzyć ciąg znaków do int w C++?

W Jaki Sposób C++ przetwarza łańcuch znaków (podany jako znak*) do int? Solidna i przejrzysta obsługa błędów jest plusem (zamiast zwracającym zero ).

 250
Author: Eugene Yokota, 2008-10-11

17 answers

W nowym C++11 są do tego funkcje: stoi, stol, stoll, stoul i tak dalej.

int myNr = std::stoi(myString);

Spowoduje wyświetlenie wyjątku w przypadku błędu konwersji.

Nawet te nowe funkcje nadal mają ten sam problem {[7] } jak zauważył Dan: z radością przekonwertują łańcuch "11x "na liczbę całkowitą"11".

Zobacz więcej: http://en.cppreference.com/w/cpp/string/basic_string/stol

 152
Author: CC.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-29 19:21:18

Czego nie robić

Oto moja pierwsza rada: nie używaj do tego stringstream . Chociaż na początku może wydawać się prosty w użyciu, przekonasz się, że musisz wykonać dużo dodatkowej pracy, jeśli chcesz solidności i dobrej obsługi błędów.

Oto podejście, które intuicyjnie wydaje się działać:]}
bool str2int (int &i, char const *s)
{
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail()) {
        // not an integer
        return false;
    }
    return true;
}

To ma poważny problem: str2int(i, "1337h4x0r") szczęśliwie zwróci true i i otrzyma wartość 1337. Możemy obejść ten problem, zapewniając tam nie ma więcej znaków w stringstream po konwersji:

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

Naprawiliśmy jeden problem, ale jest jeszcze kilka innych problemów.

Co jeśli liczba w łańcuchu nie jest bazą 10? Możemy spróbować dostosować inne bazy, ustawiając strumień na właściwy tryb (np. ss << std::hex) przed próbą konwersji. Ale to oznacza, że rozmówca musi wiedzieć a priori, na jakiej podstawie jest ten numer - i skąd rozmówca może to wiedzieć? Rozmówca nie wie co numer jest jeszcze. Oni nawet nie wiedzą, że toto liczba! Skąd można się spodziewać, że będą wiedzieć, co to za baza? Moglibyśmy po prostu nakazać, aby wszystkie liczby wprowadzane do naszych programów miały wartość podstawową 10 i odrzucać szesnastkowe lub ósemkowe dane wejściowe jako nieprawidłowe. Ale to nie jest bardzo elastyczne i wytrzymałe. Nie ma prostego rozwiązania tego problemu. Nie możesz po prostu spróbować konwersji raz dla każdej bazy, ponieważ konwersja dziesiętna zawsze się powiedzie dla liczb ósemkowych (z wiodącym zerem) i ósemek konwersja może się udać dla niektórych liczb dziesiętnych. Więc teraz musisz sprawdzić, czy nie ma wiodącego zera. Ale czekaj! Liczby szesnastkowe mogą zaczynać się od zera początkowego (0x...). Westchnienie.

Nawet jeśli uda Ci się uporać z powyższymi problemami, istnieje jeszcze jeden większy problem: co zrobić, jeśli wywołujący musi rozróżnić złe wejście (np. "123foo") i liczbę, która jest poza zakresem int (np. "4000000000" dla 32-bitowego int)? Z stringstream, nie ma sposobu, aby dokonać tego rozróżnienia. My wiadomo tylko, czy nawrócenie się powiodło,czy nie. Jeśli się nie powiedzie, nie wiemy, dlaczego się nie powiedzie. Jak widać, stringstream pozostawia wiele do życzenia, jeśli chcesz solidności i jasnej obsługi błędów.

To prowadzi mnie do mojej drugiej Rady: nie używaj Boost ' s lexical_cast do tego . Zastanów się, co ma do powiedzenia lexical_cast dokumentacja:

Gdzie wyższy stopień kontroli jest wymagane przez konwersje, std:: stringstream and std:: wstringstream oferują więcej odpowiednia ścieżka. Gdzie konwersje nie-strumieniowe to wymagane, lexical_cast jest błędem narzędzie do pracy i nie jest specjalnie dla takich scenariuszy.

Co?? Widzieliśmy już, że stringstream ma słaby poziom kontroli, a mimo to mówi, że stringstream powinien być używany zamiast lexical_cast, jeśli potrzebujesz "wyższego poziomu kontroli". Ponadto, ponieważ lexical_cast jest tylko opakowaniem stringstream, cierpi na te same problemy, które stringstream sprawia: słabe wsparcie dla wielu baz liczbowych i złej obsługi błędów.

Najlepsze rozwiązanie

[26]}na szczęście ktoś już rozwiązał wszystkie powyższe problemy. Biblioteka standardowa C zawiera strtol i rodzinę, które nie mają żadnego z tych problemów.
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;
}

Całkiem proste jak na coś, co obsługuje wszystkie przypadki błędów i obsługuje dowolną bazę liczb od 2 do 36. Jeśli base jest równe zero (domyślnie), będzie próbował przekonwertować z dowolnej bazy. Lub wywołujący może podać trzeci argument i określ, że konwersja powinna być podejmowana tylko dla określonej bazy. Jest solidny i radzi sobie ze wszystkimi błędami przy minimalnym wysiłku.

Inne Powody, dla których warto wybrać strtol (i rodzinę):

  • wykazuje znacznie lepszą wydajność pracy
  • W przeciwieństwie do poprzednich wersji, nie jest to możliwe.]}
  • daje najmniejszy rozmiar kodu

Nie ma absolutnie żadnego powodu, aby użyj innej metody.

 192
Author: Dan Moulding,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-27 16:05:21

Jest to bezpieczniejszy sposób C niż atoi ()

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

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

C++ z biblioteką standardową stringstream: (thanks CMS )

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

With boost library: (thanks 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
}

Edit: Poprawiono wersję stringstream tak, że obsługuje błędy. (dzięki komentarzowi CMS i jk w oryginalnym poście)

 65
Author: Luka Marinko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:48

Możesz użyć Boost ' S lexical_cast, który zawija ten w bardziej ogólny interfejs. lexical_cast<Target>(Source) rzuca {[2] } na porażkę.

 22
Author: jk.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:35

The good 'old C way still works. Polecam strtol lub strtoul. Pomiędzy statusem powrotu a 'endPtr' możesz dać dobre wyjście diagnostyczne. Dobrze radzi sobie również z wieloma bazami.

 20
Author: Chris Arguin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-11 19:22:28

Możesz użyć stringstream z biblioteki standardowej C++:

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

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

Stan strumienia zostanie ustawiony na fail w przypadku napotkania niecyfru, gdy próbuję odczytać liczbę całkowitą.

[1]}Zobacz pułapki strumienia dla pułapek błędów i strumieni w C++.
 15
Author: jk.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-11 19:49:49

Możesz użyć stringstream ' S

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  ss >> num;
  return num;
}
 10
Author: CMS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-11 19:26:06

Myślę, że te trzy linki sumują to:

Rozwiązania Stringstream i lexical_cast są mniej więcej takie same, jak lexical cast używa stringstream.

Niektóre specjalizacje Obsada leksykalna użyj innego podejścia zobacz http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp Po szczegóły. Liczby całkowite i pływaki są teraz wyspecjalizowane do konwersji liczb całkowitych na ciąg znaków.

Można specjalizować lexical_cast dla własnych potrzeb i zrobić to szybko. Byłoby to ostateczne rozwiązanie satysfakcjonujące wszystkie strony, czyste i proste.

Artykuły już wspomniane pokazują porównanie różnych metod konwersji liczb całkowitych. Następujące podejścia mają sens: Stary c-way, spirit.karma, fastformat, prosta naiwna pętla.

Lexical_cast jest ok w niektórych przypadkach, np. dla konwersji int na string.

Konwersja string do int za pomocą leksykalnego cast nie jest dobrym pomysłem, ponieważ jest 10-40 razy wolniejsza niż atoi w zależności od platformy / kompilatora.

/ Align = "left" / Duch.Karma wydaje się być najszybszą biblioteką do konwersji liczb całkowitych na string.
ex.: generate(ptr_char, int_, integer_number);

I podstawowa prosta pętla z wyżej wymienionego artykułu jest najszybszy sposób konwersji string do int, oczywiście nie najbezpieczniejszy, strtol () wydaje się być bezpieczniejszym rozwiązaniem

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;
}
 7
Author: caa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-05 20:20:56

Biblioteka C++ String Toolkit (StrTk) ma następujące rozwiązanie:

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 może być niepodpisanym znakiem*, znakiem* lub iteratorem std::string, a oczekuje się, że T będzie podpisaną wartością int, taką jak signed int, int lub long

 7
Author: Rup,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-25 14:30:32

Jeśli masz C++11, odpowiednimi rozwiązaniami są obecnie funkcje konwersji C++ w<string>: stoi, stol, stoul, stoll, stoull. Rzucają odpowiednie wyjątki, gdy podane są nieprawidłowe dane wejściowe i używają szybkich i małych funkcji strto* pod maską.

Jeśli utkniesz z wcześniejszą wersją C++, byłoby dla Ciebie forward-portable, aby naśladować te funkcje w Twojej implementacji.

 6
Author: fuzzyTew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-01 15:52:03

Począwszy od C++17 możesz używać {[1] } z nagłówka <charconv> jako udokumentowanego tutaj .

Na przykład:

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

Jako bonus, może również obsługiwać inne bazy, takie jak szesnastkowe.

 3
Author: Pharap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-13 17:19:07

Podoba mi się odpowiedź dana Mouldinga , dodam do niej trochę stylu 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;
}

Działa zarówno dla std:: string, jak i const char * poprzez konwersję domyślną. Jest również przydatny do konwersji bazowej, np. wszystkie to_int("0x7b") i to_int("0173") i to_int("01111011", 2) i to_int("0000007B", 16) oraz to_int("11120", 3) i to_int("3L", 34); zwrócą 123.

W przeciwieństwie do std::stoi działa w pre-C++11. Również w przeciwieństwie do std::stoi, boost::lexical_cast i stringstream rzuca wyjątki dla dziwnych ciągów, takich jak "123hohoho".

UWAGA: Ta funkcja toleruje spacje początkowe, ale nie końcowe, tzn. to_int(" 123") zwraca 123, podczas gdy to_int("123 ") rzuca wyjątek. Upewnij się, że jest to dopuszczalne dla Twojego przypadku użycia lub dostosuj kod.

Taka funkcja może być częścią STL...

 2
Author: user3925906,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-09 21:20:27

Znam trzy sposoby konwersji String do int:

Albo użyć funkcji stoi (String to int) lub po prostu przejść z Stringstream, trzeci sposób, aby przejść indywidualną konwersję, kod jest poniżej:

1. Metoda

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. Metoda

#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. metoda-ale nie dla konwersji indywidualnej

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 ;

}
 2
Author: Iqra.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-31 03:18:24

Lubię odpowiedź dana , esp ze względu na unikanie WYJĄTKÓW. W przypadku rozwoju systemów wbudowanych i innych systemów niskiego poziomu może nie być odpowiedniej struktury WYJĄTKÓW.

Dodano sprawdzanie białej spacji po prawidłowym łańcuchu...te trzy linie

    while (isspace(*end)) {
        end++;
    }


Dodano również sprawdzanie błędów parsowania.

    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }


Oto pełna funkcja..

#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;
}
 1
Author: pellucide,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:45

Możesz użyć tej zdefiniowanej metody.

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

I jeśli miałbyś przekonwertować z ciągu znaków na liczbę całkowitą, wykonałbyś następujące czynności.

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

Wynik będzie 102.

 0
Author: Boris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-31 17:30:29

Wiem, że to starsze pytanie, ale natknąłem się na nie tak wiele razy i do tej pory nadal nie znalazłem ładnie szablonowego rozwiązania o następujących cechach:

  • Może konwertować dowolną bazę (i wykrywać Typ bazy)
  • wykryje błędne dane (tj. upewni się, że cały ciąg znaków, mniej wiodących/końcowych białych znaków, jest zużywany przez konwersję)
  • zapewni, że niezależnie od typu, na który zostanie skonwertowany, zakres wartości łańcucha będzie wynosił akceptowalne.

Oto Mój, z paskiem testowym. Ponieważ używa funkcji C strtoull / strtoll pod maską, zawsze konwertuje najpierw na największy dostępny typ. Następnie, jeśli nie używasz największego typu, przeprowadzi dodatkowe kontrole zakresu, aby zweryfikować, że Twój typ nie został przekroczony (poniżej). W tym celu jest trochę mniej wydajny niż jeśli prawidłowo wybrałeś strtol / strtoul. Jednak działa również na shorts/chars i, z tego co wiem, nie ma standardowa funkcja biblioteczna, która również to robi.

Enjoy; mam nadzieję, że ktoś uzna to za przydatne.

#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 jest metodą user-land; jest przeciążona, więc można ją wywołać w następujący sposób:

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

Lub to:

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

Nienawidzę powtarzania typu int, więc wolę ten drugi. Gwarantuje to, że w przypadku zmiany typu " a " nie uzyskamy złych wyników. Chciałbym, aby kompilator mógł to rozgryźć w następujący sposób:

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

...ale C++ nie wydedukowuje powrotu szablonu typy, więc tylko tyle mogę dostać.

Implementacja jest dość prosta:

CstrtoxllWrapper zawija zarówno strtoull, jak i strtoll, wywołując cokolwiek jest konieczne na podstawie signed-ness typu szablonu i zapewniając dodatkowe gwarancje(np. ujemne wejście jest niedozwolone, jeśli unsigned i zapewnia konwersję całego ciągu znaków).

CstrtoxllWrapper jest używany przez StringToSigned i StringToUnsigned z największym typem (long long/unsigned long long) dostępnym dla kompilatora; pozwala to na maksymalne konwersja do wykonania. Następnie, jeśli jest to konieczne, StringToSigned/StringToUnsigned wykonuje ostateczne kontrole zakresu dla podstawowego typu. Na koniec, metoda punktu końcowego StringToDecimal decyduje o tym, którą z metod szablonu StringTo* wywołać na podstawie podpisanej wartości typu bazowego.

Myślę, że większość śmieci może być zoptymalizowana przez kompilator; prawie wszystko powinno być deterministyczne w czasie kompilacji. Każdy komentarz na ten temat byłby dla mnie interesujący!

 0
Author: DreamWarrior,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-03 00:41:05

W C można użyć int atoi (const char * str),

Parsuje ciąg C str interpretując jego zawartość jako liczbę całkowitą, która jest zwracana jako wartość typu int.

 -3
Author: BlackMamba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-01 16:01:15