Jaki jest sposób C ++ parsowania łańcucha (podanego jako char *) w int? Solidna i przejrzysta obsługa błędów to plus (zamiast zwracania zera ).
261
Jaki jest sposób C ++ parsowania łańcucha (podanego jako char *) w int? Solidna i przejrzysta obsługa błędów to plus (zamiast zwracania zera ).
Odpowiedzi:
W nowym C ++ 11 są do tego funkcje: stoj, stol, stoll, stoul i tak dalej.
Zgłasza wyjątek dotyczący błędu konwersji.
Nawet te nowe funkcje nadal mają ten sam problem, co zauważył Dan: z przyjemnością przekonwertują ciąg „11x” na liczbę całkowitą „11”.
Zobacz więcej: http://en.cppreference.com/w/cpp/string/basic_string/stol
źródło
size_t
nie jest równy długości łańcucha, to zatrzymał się wcześnie. W takim przypadku nadal zwróci 11, alepos
będzie 2 zamiast długości łańcucha 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29Czego nie robić
Oto moja pierwsza rada: nie używaj do tego łańcucha znaków . Choć na początku może wydawać się prosty w użyciu, okaże się, że musisz wykonać wiele dodatkowej pracy, jeśli chcesz solidności i dobrej obsługi błędów.
Oto podejście, które intuicyjnie wydaje się działać:
Ma to poważny problem:
str2int(i, "1337h4x0r")
chętnie wrócitrue
ii
otrzyma wartość1337
. Możemy obejść ten problem, upewniając się, żestringstream
po konwersji nie ma więcej znaków :Rozwiązaliśmy jeden problem, ale wciąż istnieje kilka innych problemów.
Co się stanie, jeśli liczba w ciągu nie będzie podstawową wartością 10? Możemy spróbować dostosować inne zasady, ustawiając strumień w prawidłowy tryb (np.
ss << std::hex
) Przed próbą konwersji. Ale to oznacza, że dzwoniący musi z góry wiedzieć, na jakiej podstawie jest ten numer - i skąd dzwoniący może to wiedzieć? Dzwoniący nie wie jeszcze, jaki jest numer. Nawet nie wiedzą, że tak jestnumer! Jak można oczekiwać, że będą wiedzieć, na jakiej podstawie? Możemy po prostu nakazać, aby wszystkie liczby wprowadzane do naszych programów musiały być podstawową wartością 10 i odrzucać dane szesnastkowe lub ósemkowe jako nieprawidłowe. Ale to nie jest zbyt elastyczne ani solidne. Nie ma prostego rozwiązania tego problemu. Nie można po prostu spróbować konwersji raz dla każdej podstawy, ponieważ konwersja dziesiętna zawsze powiedzie się dla liczb ósemkowych (z wiodącym zerem), a konwersja ósemkowa może się powieść dla niektórych liczb dziesiętnych. Więc teraz musisz sprawdzić wiodące zero. Ale poczekaj! Liczby szesnastkowe mogą również zaczynać się od zera na początku (0x ...). Westchnienie.Nawet jeśli uda ci się poradzić sobie z powyższymi problemami, istnieje jeszcze jeden większy problem: co, jeśli dzwoniący będzie musiał odróżnić złe wejście (np. „123foo”) od liczby spoza zakresu
int
(np. „4000000000” dla 32-bitint
)? Dziękistringstream
nie ma sposobu na dokonanie tego rozróżnienia. Wiemy tylko, czy konwersja się powiodła, czy nie. Jeśli to się nie powiedzie, nie możemy wiedzieć, dlaczego się nie udało. Jak widać,stringstream
pozostawia wiele do życzenia, jeśli chcesz solidności i wyraźnej obsługi błędów.To prowadzi mnie do mojej drugiej rady: nie używaj
lexical_cast
do tego wzmocnienia . Zastanów się, colexical_cast
ma do powiedzenia dokumentacja:Co?? Widzieliśmy już, że
stringstream
ma słaby poziom kontroli, a jednak mówi, żestringstream
należy go używać zamiast,lexical_cast
jeśli potrzebujesz „wyższego poziomu kontroli”. Ponadto, ponieważlexical_cast
jest to tylko opakowaniestringstream
, cierpi z powodu tych samych problemów,stringstream
co: słaba obsługa wielu baz danych i słaba obsługa błędów.Najlepszym rozwiązaniem
Na szczęście ktoś już rozwiązał wszystkie powyższe problemy. Biblioteka standardowa C zawiera
strtol
i rodzinę, która nie ma żadnego z tych problemów.Całkiem proste jak na coś, co obsługuje wszystkie przypadki błędów, a także obsługuje dowolną bazę liczb od 2 do 36. Jeśli
base
wynosi zero (domyślnie), spróbuje przekonwertować z dowolnej bazy. Lub osoba dzwoniąca może podać trzeci argument i określić, że konwersję należy próbować tylko dla określonej bazy. Jest solidny i obsługuje wszystkie błędy przy minimalnym wysiłku.Inne powody, aby preferować
strtol
(i rodzinę):Nie ma absolutnie żadnego powodu, aby używać innej metody.
źródło
strtol
bezpieczeństwa wątków. POSIX wymaga takżeerrno
użycia lokalnego magazynu wątków. Nawet w systemach innych niż POSIX, prawie wszystkie implementacjeerrno
systemów wielowątkowych używają pamięci lokalnej. Najnowszy standard C ++ wymagaerrno
zgodności z POSIX. Najnowszy standard C wymaga takżeerrno
przechowywania w lokalnym wątku. Nawet w systemie Windows, który zdecydowanie nie jest zgodny z POSIX,errno
jest bezpieczny dla wątków, a co za tym idzie - równieżstrtol
.std::stol
opcje, które będą odpowiednio zgłaszać wyjątki, a nie zwracać stałe.std::stol
nawet dodałem ją do języka C ++. To powiedziawszy, nie sądzę, że można uczciwie powiedzieć, że jest to „kodowanie C w C ++”. Głupie jest twierdzenie, żestd::strtol
jest to kodowanie C, gdy jest to wyraźnie część języka C ++. Moja odpowiedź idealnie pasowała do C ++, kiedy została napisana i nadal obowiązuje nawet w przypadku nowejstd::stol
. Wywoływanie funkcji, które mogą generować wyjątki, nie zawsze jest najlepsze w każdej sytuacji programowania.Jest to bezpieczniejszy sposób w C niż atoi ()
C ++ ze standardowym ciągiem znaków biblioteki : (dzięki CMS )
Z biblioteką doładowań : (dzięki jk )
Edycja: Naprawiono wersję ciągu, która obsługuje błędy. (dzięki komentarzowi CMS i jk do oryginalnego postu)
źródło
Dobry „stary” sposób nadal działa. Polecam strtol lub strtoul. Pomiędzy statusem zwracanym a „endPtr” można uzyskać dobre wyniki diagnostyczne. Ładnie obsługuje również wiele baz.
źródło
Możesz użyć wzmocnienia
lexical_cast
, które otacza to bardziej ogólny interfejs.lexical_cast<Target>(Source)
rzuca siębad_lexical_cast
na porażkę.źródło
Możesz użyć ciągu znaków ze standardowego biblioteki bibliotek C ++:
Zobacz pułapki pułapek dla pułapek obsługi błędów i strumieni w C ++.
źródło
Możesz użyć ciągu znaków
źródło
Myślę, że te trzy linki podsumowują to:
Rozwiązania stringstream i lexical_cast są mniej więcej takie same, jak rzutowanie leksykalne przy użyciu stringstream.
Niektóre specjalizacje obsady leksykalnej wykorzystują inne podejście, patrz http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp w celu uzyskania szczegółowych informacji. Liczby całkowite i zmiennoprzecinkowe specjalizują się teraz w konwersji liczb całkowitych na ciągi znaków.
Można specjalizować lexical_cast dla własnych potrzeb i sprawić, by był szybki. To byłoby najlepsze rozwiązanie zadowalające wszystkie strony, czyste i proste.
Wspomniane już artykuły pokazują porównanie różnych metod konwersji liczb całkowitych <-> ciągów. Sensowne są następujące podejścia: stara c-way, spirit.karma, fastformat, prosta naiwna pętla.
Leksyk_cast jest w niektórych przypadkach ok, np. Do konwersji int na ciąg znaków.
Konwersja napisów na int przy użyciu rzutowania leksykalnego nie jest dobrym pomysłem, ponieważ jest 10-40 razy wolniejsza niż atoi w zależności od użytej platformy / kompilatora.
Boost.Spirit.Karma wydaje się być najszybszą biblioteką do konwersji liczb całkowitych na ciąg.
a prosta prosta pętla z wyżej wymienionego artykułu jest najszybszym sposobem konwersji łańcucha znaków na int, oczywiście nie najbezpieczniejszym, strtol () wydaje się bezpieczniejszym rozwiązaniem
źródło
Biblioteka C ++ String Toolkit Library (StrTk) ma następujące rozwiązanie:
InputIterator może mieć iteratory bez znaku char *, char * lub std :: string, a oczekuje się, że T będzie znakiem int, takim jak int, int lub long
źródło
v = (10 * v) + digit;
przepełnia niepotrzebnie po wprowadzeniu ciągu o wartości tekstowejINT_MIN
. Tabela ma wątpliwą wartość vs po prostudigit >= '0' && digit <= '9'
Jeśli masz c ++ 11, odpowiednie rozwiązania w dzisiejszych czasach są C ++ całkowitą funkcji konwersji w
<string>
:stoi
,stol
,stoul
,stoll
,stoull
. Zgłaszają odpowiednie wyjątki, gdy otrzymają nieprawidłowe dane wejściowe, i używają szybkiego i małegostrto*
funkcji pod maską.Jeśli utkniesz z wcześniejszą wersją C ++, byłoby naśladowanie tych funkcji w twojej implementacji.
źródło
Od wersji C ++ 17 możesz korzystać
std::from_chars
z<charconv>
nagłówka, jak tu udokumentowano .Na przykład:
Jako bonus, może również obsługiwać inne bazy, takie jak szesnastkowy.
źródło
Podoba mi się odpowiedź Dana Mouldinga , dodam do niej trochę stylu C ++:
Działa zarówno dla std :: string, jak i const char * poprzez niejawną konwersję. Jest także przydatny do konwersji bazowej, np. Wszystkie
to_int("0x7b")
ito_int("0173")
orazto_int("01111011", 2)
ito_int("0000007B", 16)
ito_int("11120", 3)
ito_int("3L", 34);
wróci 123.W przeciwieństwie do
std::stoi
tego działa w wersjach wcześniejszych niż C ++ 11. W przeciwieństwie do równieżstd::stoi
,boost::lexical_cast
istringstream
rzuca wyjątki dziwne ciągi jak „123hohoho”.NB: Ta funkcja toleruje spacje wiodące, ale nie spacje końcowe, tzn.
to_int(" 123")
Zwraca 123, gdyto_int("123 ")
zgłasza wyjątek. Upewnij się, że jest to dopuszczalne w twoim przypadku użycia lub dostosuj kod.Taka funkcja może być częścią STL ...
źródło
Znam trzy sposoby konwersji String na int:
Albo użyj funkcji stoj (String to int), albo po prostu skorzystaj z Stringstream, trzeciego sposobu przejścia na indywidualną konwersję, kod poniżej:
1. metoda
2. metoda
3. metoda - ale nie w przypadku indywidualnej konwersji
źródło
Podoba mi się odpowiedź Dana , szczególnie ze względu na unikanie wyjątków. W przypadku rozwoju systemów wbudowanych i innych systemów niskiego poziomu może nie być dostępna odpowiednia struktura wyjątków.
Dodano sprawdzenie spacji po prawidłowym ciągu ... te trzy linie
Dodano również sprawdzanie błędów analizy.
Oto pełna funkcja ..
źródło
" "
.strtol()
nie jest określony, aby ustawić,errno
gdy konwersja nie nastąpi. Lepiej używać,if (s == end) return INCONVERTIBLE;
aby wykryć brak konwersji. A następnieif (*s == '\0' || *end != '\0')
mogą uprościć doif (*end)
2)|| l > LONG_MAX
i|| l < LONG_MIN
nie służą żadnemu celowi - nigdy nie są prawdziwe.Możesz użyć tej zdefiniowanej metody.
A jeśli chcesz przekonwertować z ciągu na liczbę całkowitą, po prostu wykonaj następujące czynności.
Wynik wyniósłby 102.
źródło
atoi
nie wydaje się być „sposobem C ++” w świetle innych odpowiedzi, takich jak zaakceptowanestd::stoi()
.Wiem, że to starsze pytanie, ale natknąłem się na to wiele razy i do tej pory nie znalazłem ładnie szablonowego rozwiązania o następujących cechach:
Oto moja, 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 sprawdzić, czy Twój typ nie został przepełniony (niedostatecznie). W tym celu jest nieco mniej wydajny niż wtedy, gdy ktoś właściwie wybrał strtol / strtoul. Działa to jednak również w przypadku skrótów / znaków i, zgodnie z moją najlepszą wiedzą, nie istnieje standardowa funkcja biblioteki, która by to robiła.
Cieszyć się; mam nadzieję, że ktoś uzna to za przydatne.
StringToDecimal
jest metodą lądu użytkownika; jest przeciążony, więc można go nazwać tak:albo to:
Nienawidzę powtarzania typu int, więc wolę ten drugi. Zapewnia to, że jeśli zmieni się rodzaj „a”, nie uzyska się złych wyników. Chciałbym, żeby kompilator mógł to rozgryźć w następujący sposób:
... ale C ++ nie przewiduje typów zwracanych szablonów, więc to najlepsze, co mogę uzyskać.
Implementacja jest dość prosta:
CstrtoxllWrapper
opakowuje oba elementystrtoull
istrtoll
wywołuje dowolne, które jest konieczne, w oparciu o podpisany typ szablonu i zapewniając dodatkowe gwarancje (np. negatywne dane wejściowe są niedozwolone, jeśli nie są podpisane, i zapewnia konwersję całego łańcucha).CstrtoxllWrapper
jest stosowany wStringToSigned
iStringToUnsigned
z największym typu (Dawno / unsigned long long) dostępny kompilator; pozwala to na wykonanie maksymalnej konwersji. Następnie, jeśli jest to konieczne,StringToSigned
/StringToUnsigned
przeprowadza ostateczne sprawdzenie zakresu dla typu bazowego. Wreszcie metoda punktu końcowego,StringToDecimal
decyduje, którą metodę szablonu StringTo * wywołać na podstawie podpisanego typu bazowego.Myślę, że większość śmieci może zostać zoptymalizowana przez kompilator; prawie wszystko powinno determinować czas kompilacji. Wszelkie komentarze na ten temat byłyby dla mnie interesujące!
źródło
long long
zamiastintmax_t
?if (ePtr != str)
. Ponadto użyjisspace((unsigned char) *ePtr)
do prawidłowej obsługi wartości ujemnych*ePtr
.W C, można użyć
int atoi (const char * str)
,Analizuje ciąg znaków C interpretując jego zawartość jako liczbę całkowitą, która jest zwracana jako wartość typu int.
źródło
atoi
wspomniałem w pytaniu, jestem tego świadomy. Pytanie wyraźnie nie dotyczy C, ale C ++. -1