Konwersje int na string i string na int

Coldpeer

Metody konwersji pomiędzy typami liczbowymi a tekstem. Opis różnych metod z języka C i różnych wersji C++

***

Wiele osób pyta się jak przekonwertować w C/C++ Int na String i odwrotnie; na naszym forum takie wątki pojawiają się średnio co dwa tygodnie a ich autorzy tłumaczą się nieraz, że "nie znaleźli w Google". Postanowiłem wyjść temu na przeciw i przedstawić sposoby wykonania tegoż mając nadzieję, że tekst zostanie dobrze wypozycjonowany w wyszukiwarkach.

[notka edytora: przez lata ten tekst był na szczycie wyszukiwań google. Niestety, prawie połowa z zastosowanych tu rozwiązań była zwyczajnie niepoprawna, i potrafiła doprowadzić do błędu programu (Undefined Behaviour). Mam nadzieję, że po tych zmianach artykuł nie tylko będzie bardziej przydatny, ale też przedstawi lepsze praktyki]

1 int na string
     1.1 [C++] std::to_string
     1.2 [C++] std::ostringstream
     1.3 [C/C++] sprintf
     1.4 [C++17] std::to_chars
     1.5 Własna funkcja
     1.6 Biblioteka Boost
2 string na int
     2.7 [C++] std::stoi
     2.8 [C++] istringstream
     2.9 [C/C++] strtol
     2.10 [C/C++] atoi
     2.11 [C/C++] sscanf
     2.12 [C++17] std::from_chars
     2.13 Własna funkcja
     2.14 Biblioteka Boost
3 Dodatkowe przykłady i obsługa błędów
     3.15 std::stoi
     3.16 strtol
     3.17 atoi
     3.18 sscanf
     3.19 [C++17] std::from_chars



int na string

[C++] std::to_string

Nagłówek: <string>

Jest to obecnie najlepsza metoda dla prostych przypadków.
Korzystamy z wprowadzonej w standardzie c++11 funkcji std::to_string,
która bezpośrednio rozwiązuje nasz problem:

    int num = 23;
    std::string str = std::to_string(num);

Ta sama funkcja może zostać wykorzystana do konwersji do stringa innych typów liczbowych, jak np. long long lub float:

    long long num_ll = 230000000000ll;
    std::string str_ll = std::to_string(num_ll);

    float num_f = 23.23f;
    std::string str_f = std::to_string(num_f);

Uwaga

Obecnie praktycznie wszystkie kompilatory wspierają standard C++11, i używają standardu c++14 jako domyślnego.
W związku z tym ta funkcja powinna być dostępna bez żadnej konfiguracji.

Jeśli kod nie kompiluje się, z błędem informującym o braku funkcji std::to_string, spróbuj włączyć standard c++11, a najlepiej zainstaluj nowy kompilator.

Na dzień pisania tego artykułu najnowsza wersja środowiska Code::Blocks [v20.03] jest udostępniana wraz z kompilatorem GCC v8.1.0, który ma domyślnie załączone wsparcie dla C++14, a zatem będzie posiadał też tę funkcję.

[C++] std::ostringstream

Nagłówek: <sstream>

Jest to metoda wykorzystująca formatowanie poprzez strumienie. Nie wymaga wsparcia dla C++11.

    int num = 42;
    std::ostringstream ss;
    ss << num ;
    std::string str = ss.str();

W metodzie tej tworzymy strumień (tutaj nazwany ss), do którego możemy przerzucać sformatowane dane (identycznie jak do std::cout),
a następnie uzyskać je wszystkie jako string korzystając z .str().

[C/C++] sprintf

Nagłówek: <cstdio> (C++), <stdio.h> (C)

    int num = 42;
    char buffer[12];
    std::sprintf(buffer, "%d", num);
    std::string str = buffer;

Funkcja sprintf pochodząca z języka C jest wyjątkowo niebezpieczna: należy uważać, aby bufor miał wystarczający rozmiar dla dowolnej wartości, którą będziemy wypisywać.

W tym przykładzie zakładamy, że int ma rozmiar nieprzekraczający 32 bitów (standard).
Wtedy potrzebujemy bufora o rozmiarze 12 (10 cyfr, opcjonalnie znak minus '-', null terminator na koniec napisu '\0').

[C++17] std::to_chars

[Zaawansowane]

Nagłówek: <charconv>

Funkcja std::to_chars wypisuje naszą liczbę jako sformatowany tekst do przygotowanego bufora charów.

    // dodatkowo zaincludeować <system_error>
    char str[10];
    int num = 42;
    std::string result;

    auto [p, ec] = std::to_chars(std::begin(str), std::end(str), num);
    if(ec == std::errc{}) // nie ma błędu
        result.assign(str, p);

Funkcja zwraca wskaźnik p za ostatni znak, który został wpisany, i kod błędu ec (std::errc{} przy braku błędu, lub std::errc::value_too_large jeśli bufor jest za mały, aby pomieścić liczbę)

Cechą szczególną tej funkcji jest niezależność od ustawień językowych (tzw. locale). Dzięki temu mamy pewność, że nawet po zmianie ustawień locale wypisana liczba nie będzie zawierała np. separatorów tysięcy '. Jest to bardziej istotne w przypadku liczb ułamkowych (kropka zawsze jako separator).

Pozwala na wypisywanie liczby w dowolnej bazie (systemie dziesiętnym, dwójkowym itd.).

Oprócz tego funkcja nie zapisuje null-terminatora na koniec tekstu w buforze.

Własna funkcja

Można jeszcze napisać własną funkcję.
Łatwo jednak ominąć przypadki skrajne, a własna implementacja nie daje wymiernych zysków.
Przez to zaleca się używanie funkcji z bibliotek standardowych, a pisanie samodzielnie takiej funkcji jedynie jako ćwiczenie.
Dla dociekliwych pokażę, jak mogłaby taka prosta implementacja wyglądać:

Zwracająca obiekt klasy string (C++)

std::string intToStr(int n)
{
    std::string ret, tmp;

    if (n < 0) {
        ret = "-";
        n = -n;
    }

    do {
        tmp += (char)(n % 10 + '0');
        n -= n % 10;
    } while(n /= 10);

    for(int i = tmp.size()-1; i >= 0; i--)
        ret += tmp[i];

    return ret;
}

Zwracająca obiekt klasy string (C++), wersja rekurencyjna

// niezwykle niewydajne, nie używać!!
std::string intToStrRec(int n)
{
    std::string ret;

    if(n < 0) {
        ret = "-";
        n = -n;
    }

    if(n > 9)
        ret+= intToStrRec(n / 10);
    ret += (char)(n % 10 + '0');

    return ret;
}

Wypisująca do bufora charów (C/C++)

// zwraca liczbę zapisanych znaków, lub -1 przy błędzie
int intToStr(int n, char* out_buffer, int out_buffer_size)
{
    int size = 0;
    int negative = n < 0;
    char tmp[12];

    if (negative)
        n = -n;

    do {
        tmp[size] = n % 10 + '0';
        size++;
        n /= 10;
    } while(n != 0);

    if (size + 1 + negative > out_buffer_size)
        return -1;

    if (negative)
        *out_buffer++ = '-';

    for (int i = size - 1; i >= 0; --i)
        *out_buffer++ = tmp[i];

    *out_buffer = '\0';

    return size;
}

Biblioteka Boost

Jeśli mamy dostęp do popularnej biblioteki Boost, możemy do konwersji wykorzystać klasę lexical_cast:

#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>

int main() {
    int value = 42;
    std::string str = boost::lexical_cast<std::string>(value);
    std::cout << str << std::endl;
}


string na int

[C++] std::stoi

Nagłówek: <string>

Jest to obecnie najlepsza metoda dla prostych przypadków.
Wprowadzona w standardzie C++11 funkcja std::stoi (string to int) bezpośrednio rozwiązuje nasz problem.

Funkcja zamienia stringa na liczbę, i ignoruje resztę znaków od pierwszej litery, jaką napotka.
Oprócz tego pozwala na określenie w jakim systemie zapisana jest liczba (domyślnie dziesiętny).

    std::string str_num = "2731";
    int num = std::stoi(str_num);
#include <iostream>   // std::cout
#include <string>     // std::string, std::stoi

int main () {
    std::string str_dec = "2001, tekst";
    std::string str_hex = "40c3";
    std::string str_bin = "-10010110001";
    std::string str_auto = "0x7f";
    std::string str_err = "tekst i liczba 44";

    int i_dec = std::stoi(str_dec);               // domyślnie w systemie dziesiętnym
    int i_hex = std::stoi(str_hex, nullptr, 16);  // w systemie szestnastkowym
    int i_bin = std::stoi(str_bin, nullptr, 2);   // w systemie dwójkowym
    int i_auto = std::stoi(str_auto, nullptr, 0); // dedukuje system => szestnastkowy (zaczyna się od 0x)
    // error: 'std::invalid_argument'
    // int i_err = std::stoi(str_err);

    std::cout << "stoi(\"" << str_dec << "\") to " << i_dec<< "\n";
    std::cout << "stoi(\"" << str_hex << "\") (hex) to " << i_hex<< "\n";
    std::cout << "stoi(\"" << str_bin << "\") (bin) to " << i_bin<< "\n";
    std::cout << "stoi(\"" << str_auto << "\") (auto) to " << i_auto<< "\n";
}

wynik:

stoi("2001, tekst") to 2001
stoi("40c3") (hex) to 16579
stoi("-10010110001") (bin) to -1201
stoi("0x7f") (auto) to 127

Znajdziemy też podobne funkcje dla pozostałych typów liczbowych:

  • std::stol dla zmiennych typu long
  • std::stoll dla zmiennych typu long long
  • std::stoul dla zmiennych typu unsigned long
  • std::stoull dla zmiennych typu unsigned long long
  • std::stof dla zmiennych typu float
  • std::stod dla zmiennych typu double
  • std::stold dla zmiennych typu long double

Dodatkowe przykłady dla tej funkcji i obsługa błędów znajdują się na dole artykułu.

Uwaga

Obecnie praktycznie wszystkie kompilatory wspierają standard C++11, i używają standardu c++14 jako domyślnego.
W związku z tym ta funkcja powinna być dostępna bez żadnej konfiguracji.

Jeśli kod nie kompiluje się, z błędem informującym o braku funkcji std::to_string, spróbuj włączyć standard c++11, a najlepiej zainstaluj nowy kompilator.

Na dzień pisania tego artykułu najnowsza wersja środowiska Code::Blocks [v20.03] jest udostępniana wraz z kompilatorem GCC v8.1.0, który ma domyślnie załączone wsparcie dla C++14, a zatem będzie posiadał też tę funkcję.

[C++] istringstream

Nagłówek: <sstream>

Jest to metoda wykorzystująca formatowanie poprzez strumienie. Nie wymaga wsparcia dla C++11.

    std::string str = "123";
    int i;
    std::istringstream iss(str);
    iss >> i;

W metodzie tej tworzymy strumień (tutaj nazwany iss) ze stringa str, z którego możemy wczytywać dane (identycznie jak z std::cin).

Ze względu na naturę strumieni metoda ta będzie znacznie wolniejsza, niż std::stoi.

[C/C++] strtol

Nagłówek: <cstdlib> (C++), <stdlib.h> (C)

Pochodząca z języka C funkcja strtol ma niemal identyczne działanie, jak opisana wyżej funkcja str::stoi.
Pracuje za to na tablicach znaków (wskaźnikach char*).

Użycie z std::string (C++)

    std::string str = "123";
    int i = strtol(str.c_str(), nullptr, 10);

Użycie z char* (C/C++)

    const char *str = "123";
    int i = strtol(str, NULL, 10);

W rzeczywistości funkcja C++ std::stoi jest jedynie prostym wrapperem przystosowującym funkcję strtol do działania z std::stringiem.
Funkcje różnią się też sposobem obsługi błędów.

Istnieją takze odpowiadające funkcje dla innych typów liczbowych:

  • strtoll dla typu long long
  • strtoul dla typu unsigned long
  • strtoull dla typu unsigned long long
  • strtof dla typu float
  • strtod dla typu double
  • strtold dla typu long double

Dodatkowe przykłady i obsługa błędów znajdują się na dole artykułu.

[C/C++] atoi

Nagłówek: <cstdlib> (C++), <stdlib.h> (C)

Pochodząca z języka C funkcja atoi jest najprostszą i prawdopodobnie najszybszą standardową metodą konwersji tekstu na liczbę. Nie posiada za to żadnej obsługi błędów, i działa tylko z systemem dziesiętnym.

Użycie z std::string (C++):

    std::string str = "123";
    int i = atoi(str.c_str());

Użycie z char* (C/C++):

    const char *str = "123";
    int i = atoi(str);

Istnieją także odpowiadające funkcje dla innych typów liczbowych:

  • atol konwertująca do typu long,
  • atoll konwertująca do typu long long,
  • atof konwertująca do typu double.

Dodatkowe przykłady znajdują się na dole artykułu

[C/C++] sscanf

Nagłówek: <cstdio> (C++), <stdio.h> (C)

Pochodząca z języka C funkcja sscanf pozwala na skanowanie sformatowanego tekstu.
Choć nie jest to najlepsze rozwiązanie, w związku z argumentami formatowania pozwala na kilka nietypowych tricków, które mogą okazać się przydatne.

Proste użycie z klasą string (C++)

    std::string str = "123";
    int num;
    sscanf(str.c_str(), "%d", &num);

Proste użycie z char* (C/C++)

    const char *str = "123";
    int num;
    sscanf(str, "%d", &num);

Wczytanie z rozpoczynającym tekstem:

    std::string str = "nr: 1234";
    int num;

    // wczyta, o ile tekst rozpoczyna się od "nr: ", tutaj ok, wczyta 1234
    sscanf(str.c_str(), "nr: %d", &num);

Wczytanie określonej liczby znaków:

    std::string str = "12345678";
    int num;

    // wczyta 5 znaków liczby, tj. liczbę 12345
    sscanf(str.c_str(), "%5d", &num);

Dodatkowe przykłady znajdują się na dole artykułu

[C++17] std::from_chars

[Zaawansowane]

Nagłówek: <charconv>

Funkcja std::from_chars wczytuje liczbę z podanego bufora charów.

    // dodatkowo zaincludeować <system_error>
    char str[10] = "7312abc";
    int result;

    auto [p, ec] = std::from_chars(std::begin(str), std::end(str), result); 
    if(ec == std::errc{})
        std::cout << result; // 7312
    // p wskazuje na znak 'a', pierwszy, który nie został wczytany

Funkcja zwraca wskaźnik p pierwszy znak, który nie został wczytany (nie był cyfrą / przekraczał zakres), i kod błędu ec (std::errc{} przy braku błędu, lub std::errc::invalid_argument jeśli w tekście nie ma liczby, lub std::errc::result_out_of_range jeśli wczytana liczba jest za duża, aby zmieścić się w danym typie liczbowym, np int)

Cechą szczególną tej funkcji jest niezależność od ustawień językowych (tzw. locale). Dzięki temu mamy pewność, że nawet po zmianie ustawień locale liczba będzie wczytana poprawnie. Jest to bardziej istotne w przypadku liczb ułamkowych (kropka zawsze jako separator).
Nie przyjmie liczb rozpoczynających się od 0x, nie dedukuje samodzielnie, w jakim systemie jest dana liczba (choć można podać), nie przyjmie liczb rozpoczynających się od +.
Dzięki tym ograniczeniom jest szybka, przy zachowaniu pełnej kontroli błędów.

Dodatkowe przykłady i obsługa błędów znajdują się na dole artykułu.

Własna funkcja

A tak mogłyby wyglądać proste implementacje funkcji:

Przyjmująca jako argument obiekt klasy string (C++)

int strToInt(const std::string& s)
{
    bool negative = false;
    int tmp = 0;
    int i = 0;
    if(s[0] == '-')
    {
        i++;
        negative = true;
    }
    while(i<s.size())
    {
      tmp = 10 * tmp + s[i] - '0';
      i++;
    }
    return negative ? -tmp : tmp;   
}

Przyjmująca jako argument char* (C/C++)

int strToInt(const char *s)
{
     int tmp = 0, m = 0;
     if(*s == '-') {
      m = 1;
      *s++;
     }
     while(*s)
      tmp = 10 * tmp + (*s++) - '0';
     return m ? -tmp : tmp;
}

Biblioteka Boost

Jeśli mamy dostęp do biblioteki Boost, możemy do konwersji wykorzystać klasę lexical_cast:

#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>

int main()
{
    std::string str = "42";
    int value = boost::lexical_cast<int>(str);
    std::cout << value << std::endl;
    return 0;
}


Dodatkowe przykłady i obsługa błędów

std::stoi

Funkcja std::stoi ignoruje spacje (białe znaki) na początku tekstu.
Następnie czyta znaki, aż znajdzie pierwszy, który nie może być fragmentem liczby.
Pozwala też na sprawdzenie który to był znak.

Wyrzuca wyjątek (jeśli nie będzie obsłużony - przerwanie działania programu) w przypadku błędu:

  • std::invalid_argument jeśli tekst nie zawiera liczby,
  • std::out_of_range jeśli liczba nie zmieściłaby się w danym typie liczbowym.
#include <iostream>
#include <string>

int main()
{
    std::string str1 = "+45";
    std::string str2 = "3.14159";
    std::string str3 = "     31337i slowa";
    std::string str4 = "slowa i 2";
    std::string str5 = "999999999999";

    int myint1 = std::stoi(str1);
    int myint2 = std::stoi(str2);

    std::size_t first_not_read;
    int myint3 = std::stoi(str3, &first_not_read);
    // first_not_read == 10, literka 'i'

    // error: 'std::invalid_argument'
    // int myint4 = std::stoi(str4);
    // error: 'std::out_of_range'
    // int myint5 = std::stoi(str5);

    std::cout << "std::stoi(\"" << str1 << "\") is " << myint1 << '\n';
    std::cout << "std::stoi(\"" << str2 << "\") is " << myint2 << '\n';
    std::cout << "std::stoi(\"" << str3 << "\") is " << myint3 << '\n';
}

wynik:

std::stoi("+45") is 45
std::stoi("3.14159") is 3
std::stoi("     31337i slowa") is 31337

Obsługa błędów:

#include <iostream>
#include <string>

int main()
{
    std::string strs[3] = { "slowa i 2", "999999999999", "1989.06.04" };

    for (int i = 0; i < 3; ++i) {
        try {
            int myint = std::stoi(strs[i]);
            std::cout << "Poprawnie zamieniono \"" << strs[i] << "\" na liczbę, rezultat: " << myint << "\n";
        }
        catch(std::invalid_argument e) {
            std::cout << "Nie udało się zamienić \"" << strs[i] << "\" na liczbę, błąd std::invalid_argument:\n" << e.what() << "\n";
        }
        catch(std::out_of_range e) {
            std::cout << "Nie udało się zamienić \"" << strs[i] << "\" na liczbę, błąd std::out_of_range:\n" << e.what() << "\n";
        }
    }
}

wynik:

Nie udało się zamienić "slowa i 2" na liczbę, błąd std::invalid_argument:
stoi
Nie udało się zamienić "999999999999" na liczbę, błąd std::out_of_range:
stoi
Poprawnie zamieniono "1989.06.04" na liczbę, rezultat: 1989

strtol

Funkcja strtol działa niemal identycznie, jak std::stoi.

Ignoruje spacje (białe znaki) na początku tekstu.
Następnie czyta znaki, aż znajdzie pierwszy, który nie może być fragmentem liczby.
Pozwala też na sprawdzenie, który to był znak.

W przypadku błędu:

  • jeśli tekst nie zawiera liczby, zwraca 0.
  • jeśli liczba nie zmieściłaby się w danym typie liczbowym, zwraca najmniejszą / największą wartość dla danego typu (LONG_MAX, LONG_MIN, LLONG_MAX lub LLONG_MIN), oraz ustawia wartość globalnej zmiennej errno na ERANGE.

Ze względu na zwracanie zera przy błędzie, jak i użycie errno, obsługa błędów jest nieco problematyczna.

#include <iostream>
#include <cstdlib>

int main()
{
    const char* str1 = "+45";
    const char* str2 = "3.14159";
    const char* str3 = "     31337i slowa";
    const char* str4 = "slowa i 2";
    const char* str5 = "99999999999999999999";

    long myint1 = strtol(str1, nullptr, 10);
    long myint2 = strtol(str2, nullptr, 10);

    char* first_not_read;
    long myint3 = strtol(str3, &first_not_read, 10);
    // first_not_read == str3 + 10, wskazuje na literkę 'i'
    long myint4 = strtol(str4, nullptr, 10);
    // zwróć uwagę, że liczby, które zmieszczą się w longu, nie muszą zmieścić się w int-cie
    long myint5 = strtol(str5, nullptr, 10);

    std::cout << "strtol(\"" << str1 << "\") is " << myint1 << '\n';
    std::cout << "strtol(\"" << str2 << "\") is " << myint2 << '\n';
    std::cout << "strtol(\"" << str3 << "\") is " << myint3 << '\n';
    std::cout << "strtol(\"" << str4 << "\") is " << myint4 << '\n';
    std::cout << "strtol(\"" << str5 << "\") is " << myint5 << '\n';
}

wynik:

strtol("+45") is 45
strtol("3.14159") is 3
strtol("     31337i slowa") is 31337
strtol("slowa i 2") is 0
strtol("99999999999999999999") is 9223372036854775807

Różne systemy liczbowe:

#include <iostream>
#include <cstdlib>

int main()
{
    const char* str_hex = "40c3";
    const char* str_bin = "-10010110001";
    const char* str_auto = "0x7f";

    int myint_hex_ = strtol(str_hex, nullptr, 16); // w systemie szestnastkowym
    int myint_bin = strtol(str_bin, nullptr, 2);   // w systemie dwójkowym
    int myint_auto = strtol(str_auto, nullptr, 0); // dedukuje system => szestnastkowy (liczba zaczyna się od 0x)

    std::cout << "strtol(\"" << str_bin << "\") in hex is " << myint_hex_ << '\n';
    std::cout << "strtol(\"" << str_bin << "\") in binary is " << myint_bin << '\n';
    std::cout << "strtol(\"" << str_auto << "\") with deduced system is " << str_auto << '\n';
}

wynik:

strtol("40c3") in hex is 16579
strtol("-10010110001") in binary is -1201
strtol("0x7f") with deduced system is 127

Obsługa błędów:

#include <iostream>
#include <cstdlib>

int main()
{
    const char* strs[3] = { "tekst i liczba 2", "9999999999999999999", "44" };
    for (int i = 0; i < 3; ++i) {
        errno = 0;
        char* reading_finished;
        long result = strtol(strs[i], &reading_finished, 10);

        // musimy sprawdzić errno pierwsze, bo 'cout' może zmienić errno
        if (errno == ERANGE) {
            std::cout << "Nie udało się zamienić tekstu \"" << strs[i] << " na liczbę, "
            "wartość nie zmieściłaby się w longu\n";
        }
        else if (reading_finished == strs[i]) { // strtol nie przeczytał nawet jednego znaku
            // tutaj result == 0
            std::cout << "Nie udało się zamienić tekstu \"" << strs[i] << " na liczbę, "
            "problem przy znaku '" << *reading_finished << "'\n";
        } else {
            std::cout << "Udało się zamienić tekst \"" << strs[i] << "\" na liczbę!\n";
        }

        std::cout << "rezultat: " << result << "\n";
    }
}

wynik:

Nie udało się zamienić tekstu "tekst i liczba 2 na liczbę, problem przy znaku 't'
rezultat: 0
Nie udało się zamienić tekstu "9999999999999999999 na liczbę, wartość nie zmieściłaby się w longu
rezultat: 9223372036854775807
Udało się zamienić tekst "44" na liczbę!
rezultat: 44

atoi

#include <iostream>
#include <cstdlib>

int main()
{
    const char* str1 = "+45";
    const char* str2 = "3.14159";
    const char* str3 = "     31337i slowa";
    const char* str4 = "slowa i 2";
    const char* str5 = "99999999999999999999";

    long myint1 = atoi(str1);
    long myint2 = atoi(str2);
    long myint3 = atoi(str3);
    long myint4 = atoi(str4);
    long myint5 = atoi(str5);

    std::cout << "atoi(\"" << str1 << "\") is " << myint1 << '\n';
    std::cout << "atoi(\"" << str2 << "\") is " << myint2 << '\n';
    std::cout << "atoi(\"" << str3 << "\") is " << myint3 << '\n';
    std::cout << "atoi(\"" << str4 << "\") is " << myint4 << '\n';
    std::cout << "atoi(\"" << str5 << "\") is " << myint5 << '\n'; // błąd, wartość może być dowolna
}

wynik:

atoi("+45") is 45
atoi("3.14159") is 3
atoi("     31337i slowa") is 31337
atoi("slowa i 2") is 0
atoi("99999999999999999999") is -1

sscanf

sscanf może służyć do wczytania liczb dowolnego typu, jednak dla każdego typu należy podać inny "znacznik":

Znacznik Format Typ zmiennej Odpowiednik strto*
%d liczba dziesiętna (unsigned?) int strtol(_, _, 10)
%i liczba całkowita (unsigned?) int strtol(_, _, 0)
%u liczba dziesiętna,
nieujemna
(unsigned?) int strtoul(_, _, 10)
%o liczba ósemkowa,
nieujemna
(unsigned?) int strtoul(_, _, 8)
%x liczba szestnastkowa,
nieujemna
(unsigned?) int strtoul(_, _, 16)
%f liczba ułamkowa float strtof(_, _)
%ld,
%li,
%lu,
%lo,
%lx
jak wyżej,
dla odpowiadających
znaczników bez 'l'
(unsigned?) long jak wyżej
%lf liczba ułamkowa double strtod(_, _)
%Lf liczba ułamkowa long double strtold(_, _)

Można za jego pomocą wczytać też kilka wartości na raz:

   const char* str = "123:45;67";
   int num1, num2, num3;

   sscanf(str, "%d:%d;%d", &num1, &num2, &num3);

wczyta 3 liczby, pierwsze dwie oddzielone dwukropkiem, następne oddzielone średnikiem. Tutaj odpowiednio 123, 45 i 67.

sscanf zwraca jedynie informację, ile argumentów udało mu się przetworzyć (w naszym przypadku - ile liczb wczytać). Jeśli nie uda mu się przeczytać żadnej, zwraca EOF. Nie możemy tak jak w przypadku std::stoi czy strtol dowiedzieć się, na którym znaku przerwał czytanie.

Obsługa błędów:

#include <iostream>
#include <cstdio>
#include <string>

void read3Numbers(const std::string& text) {
    int num1, num2, num3;

    int numbers_read = sscanf(text.c_str(), "%d#%d:%d", &num1, &num2, &num3);

    if (numbers_read != 3)
        std::cout << "Nie udalo sie przeczytac wszystkich liczb, przeczytano: "
                  << (numbers_read == EOF ? 0 : numbers_read) << "\n";
    else 
        std::cout << "Przeczytano 3 liczby: " << num1 << ", "
                  << num2 << ", " << num3 << "\n";
}  

int main() {
    read3Numbers("12#34:56");
    read3Numbers("     123#456:789");
    read3Numbers("A1#2:3");
    read3Numbers("12z#1:5");

wynik:

Przeczytano 3 liczby: 12, 34, 56
Przeczytano 3 liczby: 123, 456, 768
Nie udalo sie przeczytac wszystkich liczb, przeczytano: 0
Nie udalo sie przeczytac wszystkich liczb, przeczytano: 1

[C++17] std::from_chars

Obsługa błędów:

#include <charconv>
#include <system_error>
#include <string_view>
#include <iostream>

int strToInt(std::string_view str) {
    const char* first = str.data();
    const char* last = str.data() + str.size();

    std::cout << "Proba zamiany \"" << str << "\" na liczbe\n";

    int num;
    const std::from_chars_result res
            = std::from_chars(first, last, num);
    auto [p, ec] = res;

    std::cout << "Przeczytano " << p - first << " znakow\n";

    if (ec == std::errc{})
        std::cout << "Sukces! " << num <<"\n\n";
    else if (res.ec == std::errc::result_out_of_range)
        std::cout << "result_out_of_range " << num << "\n\n";
    else if (res.ec == std::errc::invalid_argument)
        std::cout << "invalid_agrument " << num << "\n\n";
    else
        std::cout << "nigdy tutaj nie dojdzie\n\n";

    return num;
}

int main() {
    strToInt("7");
    strToInt("12345");
    strToInt("56789abc");
    strToInt("   12345");
    strToInt("-3712");
    strToInt("+2048");
    strToInt("0x1234abf"); // przeczyta jako 0!
    strToInt("99999999999999");
}

wynik:

Proba zamiany "7" na liczbe
Przeczytano 1 znakow
Sukces! 7

Proba zamiany "12345" na liczbe
Przeczytano 5 znakow
Sukces! 12345

Proba zamiany "56789abc" na liczbe
Przeczytano 5 znakow
Sukces! 56789

Proba zamiany "   12345" na liczbe
Przeczytano 0 znakow
invalid_agrument 0

Proba zamiany "-3712" na liczbe
Przeczytano 5 znakow
Sukces! -3712

Proba zamiany "+2048" na liczbe
Przeczytano 0 znakow
invalid_agrument 0

Proba zamiany "0x1234abf" na liczbe
Przeczytano 1 znakow
Sukces! 0

Proba zamiany "99999999999999" na liczbe
Przeczytano 14 znakow
result_out_of_range 0

Inne systemy liczbowe:

#include <charconv>
#include <system_error>
#include <string_view>
#include <iostream>

int strToInt(std::string_view str, int base) {
    std::cout << "Proba zamiany \"" << str
              << "\" na liczbe w systemie " << base << "\n";

    const char* first = str.data();
    const char* last = str.data() + str.size();

    int num;
    auto [p, ec] = std::from_chars(first, last, num, base);

    std::cout << "Przeczytano " << p - first << " znakow\n";

    if (ec == std::errc{})
        std::cout << "Sukces! " << num <<"\n\n";
    else 
        std::cout << "Blad\n\n";

    return num;
}

int main() {
    strToInt("1000011", 10);
    strToInt("1000011", 2);
    strToInt("100001100111", 10);
    strToInt("100001100111", 2);
    strToInt("56789abc", 10);
    strToInt("56789abc", 16);
}

wynik:

Proba zamiany "1000011" na liczbe w systemie 10
Przeczytano 7 znakow
Sukces! 1000011

Proba zamiany "1000011" na liczbe w systemie 2
Przeczytano 7 znakow
Sukces! 67

Proba zamiany "100001100111" na liczbe w systemie 10
Przeczytano 12 znakow
Blad

Proba zamiany "100001100111" na liczbe w systemie 2
Przeczytano 12 znakow
Sukces! 2151

Proba zamiany "56789abc" na liczbe w systemie 10
Przeczytano 5 znakow
Sukces! 56789

Proba zamiany "56789abc" na liczbe w systemie 16
Przeczytano 8 znakow
Sukces! 1450744508

Zobacz też:

9 komentarzy

Kilka dodatkowych uwag, powiązanych z tematem:

  • należy uważać na kilka niebezpiecznych konstrukcji, jak np. string s; s = 65;. Instrukcje te się skompilują, ale efekt jest nieoczekiwany: liczba 65 jest zamieniana na znak o wartości 65, w tym przypadku 'A', i otrzymujemy string z jedną literką "A". Podobnie, przy s += 65 - nie dodajemy na koniec "65" tylko literkę 'A'.

  • prawie wszystkie opisane standardowe metody polegają teoretycznie na locale, czyli mogą działać inaczej w zależności od regionu i ustawień językowych. Zazwyczaj nie jest to problemem w przypadku liczb całkowitych (niektóre implementacje nawet ignorują locale), ale jeśli potrzebujemy powtarzalności przy liczbach ułamkowych należy skorzystać z std::from_chars, std::to_chars lub, jeśli te nie są dostępne, z innej biblioteki. Tutaj polecam niesamowicie szybką i przyjemną w użyciu bibliotekę fmtlib: https://github.com/fmtlib/fmt

Wielkie dzięki!!!
Właśnie potrzebna było mi taka konwersja do programu. Nie musiałem szukać i od razu tyle wersji (przy czym strtol najszybszy na string->int) :)
Prosto i na temat. Super!

Wiele razy używałem tego tematu do przypomnienia sobie konwersji. Autor omówił wiele sposobów konwersji, a pierwszy komentarz jest komentarzem niedoszłego programisty.

Proszę o pomoc...
chodzi mi o konwersję string na long int czyli ze stringu string str = "123abc"; powinienem otrzymać wynik w postaci liczby dziesiętnej np takiej 15477424852447211265. oczywiście do unsigned long long inta
co ciekawe
1) string 123 przy użyciu atol daje int 123, a powinien np 214235634763
2) string abc daje poprawną wartość czyli coś w rodzaju 154366278856
3)string 123abc daje poprzez atol lub inna 123

moge zrobic to w postaci zmiany string na binarną potem binarna 64 na long int
ale szukam bezpośredniej metody. prosze o pomoc...

To jest wiki, możesz poprawić sam klikając przycisk "Popraw ten artykuł".

Wielkie dzięki :) Akurat tego szukałem ;)

Dlaczego int i = atoi(str.c_str()); nie przerabia mi liter na jakąś wartość, tylko na 0? A liczby zapisane w string zamienia na tą samą, z tego co się orientuję to (str=1)!=(int=1), czy może się mylę...?

Sporo błędów jest w tym arcie... i ogólnie to nie ma prawa dobrze działać. W paru miejscach jest nieoptymalne użycie konstrukcji.

Zacznijmy

1)
int i = 42;
string tmp; // brzydkie rozwiązanie
itoa(i, (char*)tmp.c_str(), 10);
string str = tmp.c_str();

string jest domyślnie pusty... to jest użycie niezgodne z standardem. TAKA KONSTRUKCJA NIE POWINNA BYĆ UŻYWANA!!!

2) int i = 42;
char *str;
itoa(i, str, 10);

str wskazuje na nieznany obszar pamięci... jeśli ktoś chce tego poprawnie użyć powinien napisać:
char str[15];
itoa(i, str, 10);

3) Podobnie jest z odpowiednikami sprintfowymi...

4) Zapis: lepsze jest użycie takiej konstrukcji: int strToInt(const string &s) (jeśli ktoś nie wie dlaczego akurat tak polecam przeczytanie Megatutorialu albo porządnej książki do C++)

5) Zapis: bardzo podobnie: int strToInt(const char *s)

Reasumując osoba, która to pisała ma bardzo małe pojęcie o programowaniu w C/C++... szok i przerażenie...

KT

Z Delphi przerzucam się na c++, ale tu serce mnie boli... 10 różnego rodzaju konwersji, z czego każda wymaga kilku linijek kodu, jakby programista nie miał co robić. Pewnie, niech sam funkcje napisze...
w Delphi jest strtoint(), inttostr() i tyle....