Twoje tagi i tytuł mówią, że potrzebujesz rozwiązania w C, ale twoje pytanie brzmi C lub C ++. Który chcesz?
In silico
1
@Yann, przepraszam za zamieszanie. Wolę C.
user618677,
1
Działa, ale nie jest to zalecany sposób, ponieważ nie ma sposobu na radzenie sobie z błędami. Nigdy nie używaj tego w kodzie produkcyjnym, chyba że możesz ufać wprowadzeniu w 100%.
Uwe Geuder,
1
Zdefiniuj „lepiej” i jasno określ, dlaczego potrzebujesz innego sposobu.
Markiz Lorne
3
@EJP Aby się poprawić.
user618677,
Odpowiedzi:
185
Jest strtollepszy IMO. Również polubiłem strtonum, więc użyj go, jeśli go masz (ale pamiętaj, że nie jest przenośny):
@ trideceth12 W systemach, w których jest dostępny, należy go zadeklarować w #<stdlib.h>. Możesz jednak użyć standardowej strtoumaxalternatywy.
cnicutar
4
Ta odpowiedź nie wydaje się krótsza niż pierwszy kod pytającego.
Azurespot
11
@NoniA. Zwięzłość jest zawsze dobra, ale nie kosztem poprawności.
cnicutar
6
Nie tyle źle, co niebezpieczne. atoi () działa, jeśli dane wejściowe są prawidłowe. Ale co jeśli zrobisz atoi („cat”)? Funkcja strtol () ma zdefiniowane zachowanie, jeśli wartość nie może być reprezentowana jako długa, natomiast atoi () nie.
Daniel B.
27
Solidne strtolrozwiązanie oparte na C89
Z:
brak niezdefiniowanego zachowania (jakie można by było z atoirodziną)
bardziej rygorystyczna definicja liczby całkowitej niż strtol(np. brak wiodących białych znaków lub końcowych znaków śmieci)
klasyfikacja przypadku błędu (np. w celu przekazania użytkownikom przydatnych komunikatów o błędach)
„testsuite”
#include<assert.h>#include<ctype.h>#include<errno.h>#include<limits.h>#include<stdio.h>#include<stdlib.h>typedefenum{
STR2INT_SUCCESS,
STR2INT_OVERFLOW,
STR2INT_UNDERFLOW,
STR2INT_INCONVERTIBLE
} str2int_errno;/* Convert string s to int out.
*
* @param[out] out The converted int. Cannot be NULL.
*
* @param[in] s Input string to be converted.
*
* The format is the same as strtol,
* except that the following are inconvertible:
*
* - empty string
* - leading whitespace
* - any trailing characters that are not part of the number
*
* Cannot be NULL.
*
* @param[in] base Base to interpret string in. Same range as strtol (2 to 36).
*
* @return Indicates if the operation succeeded, or why it failed.
*/
str2int_errno str2int(int*out,char*s,int base){char*end;if(s[0]=='\0'|| isspace(s[0]))return STR2INT_INCONVERTIBLE;
errno =0;long l = strtol(s,&end, base);/* Both checks are needed because INT_MAX == LONG_MAX is possible. */if(l > INT_MAX ||(errno == ERANGE && l == LONG_MAX))return STR2INT_OVERFLOW;if(l < INT_MIN ||(errno == ERANGE && l == LONG_MIN))return STR2INT_UNDERFLOW;if(*end !='\0')return STR2INT_INCONVERTIBLE;*out = l;return STR2INT_SUCCESS;}int main(void){int i;/* Lazy to calculate this size properly. */char s[256];/* Simple case. */
assert(str2int(&i,"11",10)== STR2INT_SUCCESS);
assert(i ==11);/* Negative number . */
assert(str2int(&i,"-11",10)== STR2INT_SUCCESS);
assert(i ==-11);/* Different base. */
assert(str2int(&i,"11",16)== STR2INT_SUCCESS);
assert(i ==17);/* 0 */
assert(str2int(&i,"0",10)== STR2INT_SUCCESS);
assert(i ==0);/* INT_MAX. */
sprintf(s,"%d", INT_MAX);
assert(str2int(&i, s,10)== STR2INT_SUCCESS);
assert(i == INT_MAX);/* INT_MIN. */
sprintf(s,"%d", INT_MIN);
assert(str2int(&i, s,10)== STR2INT_SUCCESS);
assert(i == INT_MIN);/* Leading and trailing space. */
assert(str2int(&i," 1",10)== STR2INT_INCONVERTIBLE);
assert(str2int(&i,"1 ",10)== STR2INT_INCONVERTIBLE);/* Trash characters. */
assert(str2int(&i,"a10",10)== STR2INT_INCONVERTIBLE);
assert(str2int(&i,"10a",10)== STR2INT_INCONVERTIBLE);/* int overflow.
*
* `if` needed to avoid undefined behaviour
* on `INT_MAX + 1` if INT_MAX == LONG_MAX.
*/if(INT_MAX < LONG_MAX){
sprintf(s,"%ld",(longint)INT_MAX +1L);
assert(str2int(&i, s,10)== STR2INT_OVERFLOW);}/* int underflow */if(LONG_MIN < INT_MIN){
sprintf(s,"%ld",(longint)INT_MIN -1L);
assert(str2int(&i, s,10)== STR2INT_UNDERFLOW);}/* long overflow */
sprintf(s,"%ld0", LONG_MAX);
assert(str2int(&i, s,10)== STR2INT_OVERFLOW);/* long underflow */
sprintf(s,"%ld0", LONG_MIN);
assert(str2int(&i, s,10)== STR2INT_UNDERFLOW);return EXIT_SUCCESS;}
@chux dzięki! Czy możesz wyjaśnić nieco więcej, dlaczego (unsigned char)obsada może mieć znaczenie?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Kompilator IAR C ostrzega o tym l > INT_MAXi l < INT_MINjest bezcelowym porównywaniem liczb całkowitych, ponieważ oba wyniki są zawsze fałszywe. Co się stanie, jeśli zmienię je l >= INT_MAXi l <= INT_MINskasuję ostrzeżenia? W ARM C long i int są 32-bitowymi podstawowymi typami danych w ARM C i C ++
ecle
Zmiana kodu @ecle na l >= INT_MAXniepoprawną funkcjonalność: Przykład powrotu STR2INT_OVERFLOWz wejściem "32767"i 16-bit int. Użyj kompilacji warunkowej. Przykład .
chux - Przywróć Monikę
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;byłoby lepiej, if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}aby umożliwić wywołanie kodu do używania errnona intout-of-zakresu. To samo dotyczy if (l < INT_MIN....
chux - Przywróć Monikę
24
Nie używaj funkcji z ato...grupy. Są zepsute i praktycznie bezużyteczne. Użycie byłoby lepszym rozwiązaniem sscanf, choć nie jest ono również idealne.
Aby przekonwertować ciąg na liczbę całkowitą, strto...należy użyć funkcji z grupy. W twoim konkretnym przypadku będzie to strtolfunkcja.
sscanffaktycznie ma niezdefiniowane zachowanie, jeśli próbuje przekonwertować liczbę spoza zakresu swojego typu (na przykład sscanf("999999999999999999999", "%d", &n)).
Keith Thompson,
1
@Keith Thompson: Właśnie o to mi chodzi. atoinie zapewnia żadnych istotnych informacji zwrotnych na temat sukcesu / niepowodzenia i ma niezdefiniowane zachowanie w przypadku przepełnienia. sscanfzapewnia informacje zwrotne o sukcesach / niepowodzeniach (wartość zwracana, co czyni ją „umiarkowanie lepszą”), ale nadal ma niezdefiniowane zachowanie w przypadku przepełnienia. Tylko strtolrealne rozwiązanie.
AnT
1
Zgoda; Chciałem tylko podkreślić potencjalnie śmiertelny problem sscanf. (Chociaż przyznaję, że czasami używam atoi, zwykle w przypadku programów, które nie spodziewają się przeżyć dłużej niż 10 minut przed usunięciem źródła).
Keith Thompson
5
Możesz dla zabawy napisać trochę atoi ():
int my_getnbr(char*str){int result;int puiss;
result =0;
puiss =1;while(('-'==(*str))||((*str)=='+')){if(*str =='-')
puiss = puiss *-1;
str++;}while((*str >='0')&&(*str <='9')){
result =(result *10)+((*str)-'0');
str++;}return(result * puiss);}
Możesz także ustawić go jako rekurencyjny, który może być stary w 3 liniach =)
Dziękuję bardzo .. Ale czy możesz mi powiedzieć, jak działa poniższy kod? code((* str) - „0”)code
user618677
postać ma wartość ascii. Jeśli nie masz Linuksa, wpisz: man ascii w powłoce lub jeśli nie, przejdź do: table-ascii.com . Zobaczysz, że znak „0” = 68 (myślę) dla int. Aby uzyskać liczbę „9” (to „0” + 9), otrzymujesz 9 = „9” - „0”. Czaisz?
jDourlens,
1
1) Kod pozwala "----1" 2) Ma niezdefiniowane zachowanie z intprzepełnieniem, kiedy wynik powinien być INT_MIN. Zastanów sięmy_getnbr("-2147483648")
chux - Przywróć Monikę
Dzięki za precyzję, to tylko za pokazanie małego przykładu. Jak powiedziano, dla zabawy i nauki. Zdecydowanie powinieneś używać standardowej biblioteki lib do tego rodzaju zadań. Szybciej i bezpieczniej!
jDourlens
2
Chciałem tylko udostępnić rozwiązanie dla niepodpisanego na długo.
unsignedlongToUInt(char* str){unsignedlong mult =1;unsignedlong re =0;int len = strlen(str);for(int i = len -1; i >=0; i--){
re = re +((int)str[i]-48)*mult;
mult = mult*10;}return re;}
int atoi(constchar* str){int num =0;int i =0;bool isNegetive =false;if(str[i]=='-'){
isNegetive =true;
i++;}while(str[i]&&(str[i]>='0'&& str[i]<='9')){
num = num *10+(str[i]-'0');
i++;}if(isNegetive) num =-1* num;return num;}
#include<stdio.h>#include<string.h>#include<math.h>int my_atoi(constchar* snum){int idx, strIdx =0, accum =0, numIsNeg =0;constunsignedint NUMLEN =(int)strlen(snum);/* Check if negative number and flag it. */if(snum[0]==0x2d)
numIsNeg =1;for(idx = NUMLEN -1; idx >=0; idx--){/* Only process numbers from 0 through 9. */if(snum[strIdx]>=0x30&& snum[strIdx]<=0x39)
accum +=(snum[strIdx]-0x30)* pow(10, idx);
strIdx++;}/* Check flag to see if originally passed -ve number and convert result if so. */if(!numIsNeg)return accum;elsereturn accum *-1;}int main(){/* Tests... */
printf("Returned number is: %d\n", my_atoi("34574"));
printf("Returned number is: %d\n", my_atoi("-23"));return0;}
Ale dlaczego? To nie sprawdza przepełnienia i po prostu ignoruje wartości śmieci. Nie ma powodu, aby nie korzystać z strto...rodziny funkcji. Są przenośne i znacznie lepsze.
czad
1
Dziwne w użyciu 0x2d, 0x30zamiast '-', '0'. Nie pozwala na '+'znak. Po co (int)rzucać (int)strlen(snum)? UB, jeśli wejście jest "". UB, gdy wynik jest INT_MINspowodowany intprzepełnieniemaccum += (snum[strIdx] - 0x30) * pow(10, idx);
chux - Przywróć Monikę
@chux - Ten kod to kod demonstracyjny. Istnieją proste poprawki do tego, co opisałeś jako potencjalne problemy.
ButchDean
2
@ButchDean To, co określisz jako „kod demonstracyjny”, będzie używane przez innych, którzy nie mają pojęcia o wszystkich szczegółach. Teraz chronią je tylko wynik ujemny i komentarze do tej odpowiedzi. Moim zdaniem „kod demonstracyjny” musi mieć znacznie wyższą jakość.
Roland Illig,
@RolandIllig Zamiast być krytycznym, czy nie byłoby bardziej pomocne dla innych, aby rzeczywiście stworzyć własne rozwiązanie?
ButchDean
-1
Ta funkcja ci pomoże
int strtoint_n(char* str,int n){int sign =1;int place =1;int ret =0;int i;for(i = n-1; i >=0; i--, place *=10){int c = str[i];switch(c){case'-':if(i ==0) sign =-1;elsereturn-1;break;default:if(c >='0'&& c <='9') ret +=(c -'0')* place;elsereturn-1;}}return sign * ret;}int strtoint(char* str){char* temp = str;int n =0;while(*temp !='\0'){
n++;
temp++;}return strtoint_n(str, n);}
Dlaczego to robisz? Jednym z największych problemów atoii przyjaciół jest to, że jeśli występuje przepełnienie, jest to niezdefiniowane zachowanie. Twoja funkcja tego nie sprawdza. strtoli przyjaciele tak.
czad
1
Tak. Ponieważ C nie jest Pythonem, mam nadzieję, że ludzie używający języka C są świadomi tego rodzaju błędów przepełnienia. Wszystko ma swoje granice.
Amith Chinthaka,
-1
Ok, miałem ten sam problem. Wymyśliłem to rozwiązanie, działało dla mnie najlepiej, próbowałem atoi (), ale nie działało dobrze dla mnie, więc oto moje rozwiązanie:
void splitInput(int arr[],int sizeArr,char num[]){for(int i =0; i < sizeArr; i++)// We are subtracting 48 because the numbers in ASCII starts at 48.
arr[i]=(int)num[i]-48;}
//I think this way we could go :int my_atoi(constchar* snum){int nInt(0);int index(0);while(snum[index]){if(!nInt)
nInt=((int) snum[index])-48;else{
nInt =(nInt *=10)+((int) snum[index]-48);}
index++;}return(nInt);}int main(){
printf("Returned number is: %d\n", my_atoi("676987"));return0;}
Jeśli chcesz to zrobić bezpiecznie, strtol()faktycznie wymaga sporej ilości kodu. Może on powrócić LONG_MINalbo LONG_MAXteż , jeśli to wartość rzeczywista przerobiony lub nie występuje niedomiar lub nadmiar, i może zwrócić 0 albo jeśli jest to wartość rzeczywistą lub jeśli nie ma numer konwersji. Musisz ustawić errno = 0przed połączeniem i sprawdzić endptr.
Keith Thompson,
Rozwiązania podane do analizy, nie są realnymi rozwiązaniami.
Odpowiedzi:
Jest
strtol
lepszy IMO. Również polubiłemstrtonum
, więc użyj go, jeśli go masz (ale pamiętaj, że nie jest przenośny):EDYTOWAĆ
Możesz być także zainteresowany
strtoumax
istrtoimax
które są standardowymi funkcjami w C99. Na przykład możesz powiedzieć:W każdym razie trzymaj się z dala od
atoi
:źródło
strtonum
?#<stdlib.h>
. Możesz jednak użyć standardowejstrtoumax
alternatywy.Solidne
strtol
rozwiązanie oparte na C89Z:
atoi
rodziną)strtol
(np. brak wiodących białych znaków lub końcowych znaków śmieci)GitHub w górę .
Na podstawie: https://stackoverflow.com/a/6154614/895245
źródło
str2int()
. Pedantyczny: użycieisspace((unsigned char) s[0])
.(unsigned char)
obsada może mieć znaczenie?l > INT_MAX
il < INT_MIN
jest bezcelowym porównywaniem liczb całkowitych, ponieważ oba wyniki są zawsze fałszywe. Co się stanie, jeśli zmienię jel >= INT_MAX
il <= INT_MIN
skasuję ostrzeżenia? W ARM C long i int są 32-bitowymi podstawowymi typami danych w ARM C i C ++l >= INT_MAX
niepoprawną funkcjonalność: Przykład powrotuSTR2INT_OVERFLOW
z wejściem"32767"
i 16-bitint
. Użyj kompilacji warunkowej. Przykład .if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;
byłoby lepiej,if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}
aby umożliwić wywołanie kodu do używaniaerrno
naint
out-of-zakresu. To samo dotyczyif (l < INT_MIN...
.Nie używaj funkcji z
ato...
grupy. Są zepsute i praktycznie bezużyteczne. Użycie byłoby lepszym rozwiązaniemsscanf
, choć nie jest ono również idealne.Aby przekonwertować ciąg na liczbę całkowitą,
strto...
należy użyć funkcji z grupy. W twoim konkretnym przypadku będzie tostrtol
funkcja.źródło
sscanf
faktycznie ma niezdefiniowane zachowanie, jeśli próbuje przekonwertować liczbę spoza zakresu swojego typu (na przykładsscanf("999999999999999999999", "%d", &n)
).atoi
nie zapewnia żadnych istotnych informacji zwrotnych na temat sukcesu / niepowodzenia i ma niezdefiniowane zachowanie w przypadku przepełnienia.sscanf
zapewnia informacje zwrotne o sukcesach / niepowodzeniach (wartość zwracana, co czyni ją „umiarkowanie lepszą”), ale nadal ma niezdefiniowane zachowanie w przypadku przepełnienia. Tylkostrtol
realne rozwiązanie.sscanf
. (Chociaż przyznaję, że czasami używamatoi
, zwykle w przypadku programów, które nie spodziewają się przeżyć dłużej niż 10 minut przed usunięciem źródła).Możesz dla zabawy napisać trochę atoi ():
Możesz także ustawić go jako rekurencyjny, który może być stary w 3 liniach =)
źródło
code
((* str) - „0”)code
"----1"
2) Ma niezdefiniowane zachowanie zint
przepełnieniem, kiedy wynik powinien byćINT_MIN
. Zastanów sięmy_getnbr("-2147483648")
Chciałem tylko udostępnić rozwiązanie dla niepodpisanego na długo.
źródło
const char *
.48
znaczy? Czy zakładasz, że jest to wartość miejsca, w'0'
którym kod będzie działał? Proszę nie narzucać światu tak szerokich założeń!'0'
tak, jak powinieneś.źródło
Zawsze możesz rzucić własne!
To zrobi, co chcesz bez bałaganu.
źródło
strto...
rodziny funkcji. Są przenośne i znacznie lepsze.0x2d, 0x30
zamiast'-', '0'
. Nie pozwala na'+'
znak. Po co(int)
rzucać(int)strlen(snum)
? UB, jeśli wejście jest""
. UB, gdy wynik jestINT_MIN
spowodowanyint
przepełnieniemaccum += (snum[strIdx] - 0x30) * pow(10, idx);
Ta funkcja ci pomoże
Patrz: http://amscata.blogspot.com/2013/09/strnumstr-version-2.html
źródło
atoi
i przyjaciół jest to, że jeśli występuje przepełnienie, jest to niezdefiniowane zachowanie. Twoja funkcja tego nie sprawdza.strtol
i przyjaciele tak.Ok, miałem ten sam problem. Wymyśliłem to rozwiązanie, działało dla mnie najlepiej, próbowałem atoi (), ale nie działało dobrze dla mnie, więc oto moje rozwiązanie:
źródło
źródło
nInt = (nInt *= 10) + ((int) snum[index] - 48);
vs.nInt = nInt*10 + snum[index] - '0';
if(!nInt)
nie jest potrzebny.W C ++ możesz użyć takiej funkcji:
Może to pomóc w konwersji dowolnego łańcucha na dowolny typ, taki jak float, int, double ...
źródło
Tak, możesz zapisać liczbę całkowitą bezpośrednio:
Jeśli musisz przeanalizować ciąg znaków
atoi
lubstrol
wygrywa konkurs „Najkrótsza ilość kodu”.źródło
strtol()
faktycznie wymaga sporej ilości kodu. Może on powrócićLONG_MIN
alboLONG_MAX
też , jeśli to wartość rzeczywista przerobiony lub nie występuje niedomiar lub nadmiar, i może zwrócić 0 albo jeśli jest to wartość rzeczywistą lub jeśli nie ma numer konwersji. Musisz ustawićerrno = 0
przed połączeniem i sprawdzićendptr
.