Sus etiquetas y título dicen que quiere una solución en C, pero su pregunta dice C o C ++. ¿Cuál quieres?
En silico
1
@ Yann, Perdón por esa confusión. Prefiero C.
user618677
1
Funciona, pero no es la forma recomendada, porque no hay forma de manejar los errores. Nunca use esto en el código de producción a menos que pueda confiar en la entrada al 100%.
Uwe Geuder
1
Defina "mejor" e indique claramente por qué necesita otra forma.
Marqués de Lorne
3
@EJP Solo para mejorarme.
user618677
Respuestas:
185
Hay strtolcuál es mejor IMO. También me ha gustado mucho strtonum, así que úsalo si lo tienes (pero recuerda que no es portátil):
¿Qué necesito incluir strtonum? Sigo recibiendo una advertencia de declaración implícita
jsj
@ trideceth12 En los sistemas donde está disponible, debe declararse en #<stdlib.h>. Sin embargo, podría usar la strtoumaxalternativa estándar .
cnicutar
44
Esta respuesta no parece más corta que el primer código del interlocutor.
Azurespot
11
@NoniA. La concisión siempre es buena, pero no a expensas de la corrección.
cnicutar
66
No es tan malo como inseguro. atoi () funciona si la entrada es válida. Pero, ¿y si haces atoi ("gato")? strtol () tiene un comportamiento definido si el valor no se puede representar como largo, atoi () no.
Daniel B.
27
Solución robusta strtolbasada en C89
Con:
sin comportamiento indefinido (como se podría tener con la atoifamilia)
una definición más estricta de entero que strtol(por ejemplo, sin espacios en blanco iniciales ni caracteres de basura finales)
clasificación del caso de error (por ejemplo, para dar mensajes de error útiles a los usuarios)
un "traje de prueba"
#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;}
Agradable robusto str2int(). Pedantic: uso isspace((unsigned char) s[0]).
chux - Restablece a Monica
@chux gracias! ¿Puedes explicar un poco más por qué el (unsigned char)elenco podría marcar la diferencia?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
El compilador IAR C advierte eso l > INT_MAXy l < INT_MINson una comparación de enteros sin sentido ya que cualquiera de los resultados es siempre falso. ¿Qué sucede si los cambio l >= INT_MAXy l <= INT_MINborro las advertencias? En ARM C, long e int son tipos de datos básicos con
ecle
@ecle cambiando el código para incurrir l >= INT_MAXen una funcionalidad incorrecta: Ejemplo que regresa STR2INT_OVERFLOWcon entrada "32767"y 16 bits int. Use una compilación condicional. Ejemplo .
chux
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;sería mejor como if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}para permitir llamar a código para utilizar errnoen intfuera de rango. Lo mismo para if (l < INT_MIN....
chux - Restablecer Monica
24
No uses funciones del ato...grupo. Estos están rotos y prácticamente inútiles. Sería una solución moderadamente mejor sscanf, aunque tampoco es perfecta.
Para convertir una cadena en un entero, se strto...deben usar las funciones del grupo. En su caso específico sería strtolfunción.
sscanfen realidad tiene un comportamiento indefinido si intenta convertir un número fuera del rango de su tipo (por ejemplo, sscanf("999999999999999999999", "%d", &n)).
Keith Thompson
1
@Keith Thompson: Eso es exactamente lo que quiero decir. atoino proporciona comentarios significativos de éxito / fracaso y tiene un comportamiento indefinido en caso de desbordamiento. sscanfproporciona comentarios de éxito / fracaso (el valor de retorno, que es lo que lo hace "moderadamente mejor"), pero aún tiene un comportamiento indefinido en caso de desbordamiento. Solo strtoles una solución viable.
ANT
1
Convenido; Solo quería enfatizar el problema potencialmente fatal con sscanf. (Aunque confieso que a veces uso atoi, generalmente para programas que no espero sobrevivir más de 10 minutos antes de eliminar la fuente.)
Keith Thompson
5
Puedes codificar un poco de atoi () por diversión:
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);}
También puede hacerlo recursivo, lo que puede envejecer en 3 líneas =)
un personaje tiene un valor ascii. Si eres uner tipo linux: man ascii en el shell o si no, ve a: table-ascii.com . Verá que el carácter '0' = 68 (creo) para un int. Entonces, para obtener el número de '9' (es '0' + 9) para obtener 9 = '9' - '0'. ¿Usted lo consigue?
jDourlens
1
1) El código permite "----1" 2) Tiene un comportamiento indefinido con intdesbordamiento cuando el resultado debería ser INT_MIN. Consideremy_getnbr("-2147483648")
chux
Gracias por la precisión, fue solo por mostrar un pequeño ejemplo. Como se dice por diversión y aprendizaje. Definitivamente debe usar standart lib para este tipo de tareas. ¡Más rápido y más seguro!
jDourlens
2
Solo quería compartir una solución por tiempo sin firmar también.
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;}
No maneja el desbordamiento. Además, el parámetro debería ser const char *.
Roland Illig
2
Además, ¿qué 48significa eso ? ¿Asume que ese es el valor de '0'dónde se ejecutará el código? ¡Por favor, no infundas suposiciones tan amplias en el mundo!
Toby Speight
@TobySpeight Sí, supongo que 48 representan '0' en la tabla ASCII.
Jacob
3
No todo el mundo es ASCII, solo úsalo '0'como deberías.
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;}
¿Pero por qué? Esto no comprueba el desbordamiento y simplemente ignora los valores basura. No hay razón para no usar la strto...familia de funciones. Son portátiles y significativamente mejores.
chad
1
Extraño de usar en 0x2d, 0x30lugar de '-', '0'. No permite '+'firmar. ¿Por qué (int)echarlo (int)strlen(snum)? UB si la entrada es "". UB cuando el resultado se INT_MINdeba a intdesbordar conaccum += (snum[strIdx] - 0x30) * pow(10, idx);
chux - Reinstale a Monica
@chux: este código es un código de demostración. Hay soluciones fáciles a lo que describió como problemas potenciales.
ButchDean
2
@ButchDean Lo que usted describe como "código de demostración" será utilizado por otros que no tienen idea de todos los detalles. Solo el puntaje negativo y los comentarios sobre esta respuesta los protegen ahora. En mi opinión, el "código de demostración" debe tener una calidad mucho mayor.
Roland Illig
@RolandIllig En lugar de ser todo crítico, ¿no sería más útil para otros presentar su propia solución?
ButchDean
-1
Esta función te ayudará
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);}
¿Por qué hacer esto sin embargo? Uno de los mayores problemas atoiy amigos es que si hay un desbordamiento, es un comportamiento indefinido. Su función no verifica esto. strtoly amigos lo hacen.
chad
1
Sip. Como C no es Python, espero que las personas que usan lenguaje C estén al tanto de este tipo de errores de desbordamiento. Todo tiene sus propios límites.
Amith Chinthaka
-1
Ok, tuve el mismo problema. Se me ocurrió esta solución. Funcionó mejor para mí. Intenté con atoi () pero no funcionó bien para mí. Así que aquí está mi solución:
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;}
Si desea hacerlo de forma segura, en strtol()realidad requiere una buena cantidad de código. Puede volver LONG_MINo LONG_MAXbien si ese es el valor convertido actual o si hay un desbordamiento o desbordamiento, y puede devolver 0 o bien si ese es el valor real o si no había un número para convertir. Debe configurar errno = 0antes de la llamada y verificar el endptr.
Keith Thompson
Las soluciones dadas para analizar, no son soluciones viables.
Respuestas:
Hay
strtol
cuál es mejor IMO. También me ha gustado muchostrtonum
, así que úsalo si lo tienes (pero recuerda que no es portátil):EDITAR
También puede interesarle
strtoumax
ystrtoimax
cuáles son las funciones estándar en C99. Por ejemplo, podrías decir:De todos modos, mantente alejado de
atoi
:fuente
strtonum
? Sigo recibiendo una advertencia de declaración implícita#<stdlib.h>
. Sin embargo, podría usar lastrtoumax
alternativa estándar .Solución robusta
strtol
basada en C89Con:
atoi
familia)strtol
(por ejemplo, sin espacios en blanco iniciales ni caracteres de basura finales)GitHub aguas arriba .
Basado en: https://stackoverflow.com/a/6154614/895245
fuente
str2int()
. Pedantic: usoisspace((unsigned char) s[0])
.(unsigned char)
elenco podría marcar la diferencia?l > INT_MAX
yl < INT_MIN
son una comparación de enteros sin sentido ya que cualquiera de los resultados es siempre falso. ¿Qué sucede si los cambiol >= INT_MAX
yl <= INT_MIN
borro las advertencias? En ARM C, long e int son tipos de datos básicos conl >= INT_MAX
en una funcionalidad incorrecta: Ejemplo que regresaSTR2INT_OVERFLOW
con entrada"32767"
y 16 bitsint
. Use una compilación condicional. Ejemplo .if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;
sería mejor comoif (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}
para permitir llamar a código para utilizarerrno
enint
fuera de rango. Lo mismo paraif (l < INT_MIN...
.No uses funciones del
ato...
grupo. Estos están rotos y prácticamente inútiles. Sería una solución moderadamente mejorsscanf
, aunque tampoco es perfecta.Para convertir una cadena en un entero, se
strto...
deben usar las funciones del grupo. En su caso específico seríastrtol
función.fuente
sscanf
en realidad tiene un comportamiento indefinido si intenta convertir un número fuera del rango de su tipo (por ejemplo,sscanf("999999999999999999999", "%d", &n)
).atoi
no proporciona comentarios significativos de éxito / fracaso y tiene un comportamiento indefinido en caso de desbordamiento.sscanf
proporciona comentarios de éxito / fracaso (el valor de retorno, que es lo que lo hace "moderadamente mejor"), pero aún tiene un comportamiento indefinido en caso de desbordamiento. Solostrtol
es una solución viable.sscanf
. (Aunque confieso que a veces usoatoi
, generalmente para programas que no espero sobrevivir más de 10 minutos antes de eliminar la fuente.)Puedes codificar un poco de atoi () por diversión:
También puede hacerlo recursivo, lo que puede envejecer en 3 líneas =)
fuente
code
((* str) - '0')code
"----1"
2) Tiene un comportamiento indefinido conint
desbordamiento cuando el resultado debería serINT_MIN
. Consideremy_getnbr("-2147483648")
Solo quería compartir una solución por tiempo sin firmar también.
fuente
const char *
.48
significa eso ? ¿Asume que ese es el valor de'0'
dónde se ejecutará el código? ¡Por favor, no infundas suposiciones tan amplias en el mundo!'0'
como deberías.fuente
¡Siempre puedes rodar el tuyo!
Esto hará lo que quieras sin desorden.
fuente
strto...
familia de funciones. Son portátiles y significativamente mejores.0x2d, 0x30
lugar de'-', '0'
. No permite'+'
firmar. ¿Por qué(int)
echarlo(int)strlen(snum)
? UB si la entrada es""
. UB cuando el resultado seINT_MIN
deba aint
desbordar conaccum += (snum[strIdx] - 0x30) * pow(10, idx);
Esta función te ayudará
Ref: http://amscata.blogspot.com/2013/09/strnumstr-version-2.html
fuente
atoi
y amigos es que si hay un desbordamiento, es un comportamiento indefinido. Su función no verifica esto.strtol
y amigos lo hacen.Ok, tuve el mismo problema. Se me ocurrió esta solución. Funcionó mejor para mí. Intenté con atoi () pero no funcionó bien para mí. Así que aquí está mi solución:
fuente
fuente
nInt = (nInt *= 10) + ((int) snum[index] - 48);
vs.nInt = nInt*10 + snum[index] - '0';
if(!nInt)
no es necesario.En C ++, puede usar una función de este tipo:
Esto puede ayudarlo a convertir cualquier cadena a cualquier tipo, como float, int, double ...
fuente
Sí, puede almacenar el número entero directamente:
Si debe analizar una cadena
atoi
ostrol
va a ganar el concurso de "menor cantidad de código".fuente
strtol()
realidad requiere una buena cantidad de código. Puede volverLONG_MIN
oLONG_MAX
bien si ese es el valor convertido actual o si hay un desbordamiento o desbordamiento, y puede devolver 0 o bien si ese es el valor real o si no había un número para convertir. Debe configurarerrno = 0
antes de la llamada y verificar elendptr
.