2010-08-06 27 views
56

gcc 4.4.4 c89¿Cuál es la diferencia entre sscanf o atoi para convertir una cadena en un número entero?

Qué es mejor para convertir una cadena en un valor entero.

He intentado 2 métodos diferentes atoi y sscanf. Ambos funcionan como se esperaba.

char digits[3] = "34"; 
int device_num = 0; 

if(sscanf(digits, "%d", &device_num) == EOF) { 
    fprintf(stderr, "WARNING: Incorrect value for device\n"); 
    return FALSE; 
} 

o el uso de atoi

device_num = atoi(digits); 

Estaba pensando que el sscanf sería mejor como se puede comprobar si hay errores. Sin embargo, atoi no hace ninguna comprobación.

+0

Posible duplicado de [Conversión de cadena en entero C] (http://stackoverflow.com/questions/7021725/converting-string-to-integer- c) –

Respuesta

98

Usted tiene 3 opciones:

  1. atoi

Esta es probablemente la más rápida si se está usando en el código de rendimiento crítico, pero lo hace sin el informe de errores. Si la cadena no comienza con un entero, devolverá 0. Si la cadena contiene basura después del entero, convertirá la parte inicial e ignorará el resto. Si el número es demasiado grande para caber en int, el comportamiento no está especificado.

  1. sscanf

Algunos informes de errores, y que tiene una gran flexibilidad para qué tipo de tienda (versiones firmadas/sin signo de char/short/int/long/long long/size_t/ptrdiff_t/intmax_t).

El valor de retorno es el número de conversiones que se realizan correctamente, por lo que al escanear "%d" se devolverá 0 si la cadena no comienza con un número entero. Puede usar "%d%n" para almacenar el índice del primer carácter después del número entero que se lee en otra variable, y así verificar si se convirtió la cadena completa o si hay basura después. Sin embargo, como atoi, el comportamiento en el desbordamiento de enteros no está especificado.

  1. strtol y familiares

informe de errores robusto, con tal que se establece errno a 0 antes de realizar la llamada. Los valores de retorno se especifican en overflow y se establecerá errno. Puede elegir cualquier base numérica del 2 al 36, o especificar 0 como la base para auto interpretar los 0x y 0 como hex y octales, respectivamente. Las opciones de tipo para convertir a son versiones firmadas/sin firmar de long/long long/intmax_t.

Si necesita un tipo más pequeño siempre puede almacenar el resultado en una variable temporal long o unsigned long y comprobar el desbordamiento usted mismo.

Dado que estas funciones toman un puntero al argumento del puntero, también se obtiene un puntero al primer carácter que sigue al entero convertido, de forma gratuita, para que pueda decir si la cadena entera era un entero o analizar datos posteriores en la cadena si necesario.


Personalmente, yo recomendaría la familia strtol para la mayoría de propósitos. Si estás haciendo algo rápido y sucio, atoi podría satisfacer tus necesidades.

Como nota aparte, a veces me parece que necesito analizar los números en los que no se supone que se acepten espacios en blanco iniciales, signos, etc. En este caso es muy muy fácil de rodar su propio bucle, por ejemplo,

for (x=0; (unsigned)*s-'0'<10; s++) 
    x=10*x+(*s-'0'); 

o puede utilizar (por robustez):.

if (isdigit(*s)) 
    x=strtol(s, &s, 10); 
else /* error */ 
+0

El 'errno' en 'strtol' es una característica específica de implementación como se indica en la página de manual de strtol (3). Para validar correctamente, debe pasar endptr. Si ** endptr es '\ 0' después de 'strtol', entonces la cadena se analiza como un todo y es válida (o su longitud es cero). – Zouppen

+1

@Zouppen: No tengo idea de dónde conseguiste esa información, pero está mal. "Las funciones strtol, strtoll, strtoul y strtoull devuelven el valor convertido, si corresponde. Si no se puede realizar ninguna conversión, se devuelve cero.Si el valor correcto está fuera del rango de valores representables, se devuelve LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX o ULLONG_MAX (de acuerdo con el tipo de devolución y el signo del valor, si corresponde), y el valor de la macro ERANGE es almacenado en errno. "(C99 7.20.1.4 párrafo 8) –

+0

Sin embargo, tiene razón en que necesita verificar otras condiciones. Solo el desbordamiento es un" error ". La falla de realizar cualquier conversión debe detectarse a través de' endptr', y si insista en que se consuma toda la cadena, también debe verificarlo. –

9

*scanf() familia de funciones devuelve el número de valores convertidos. Por lo tanto, debe verificar para asegurarse de que sscanf() devuelva 1 en su caso. EOF se devuelve por "error de entrada", lo que significa que ssacnf() nunca devolverá EOF.

Para sscanf(), la función tiene que analizar la cadena de formato y decodificar un entero. atoi() no tiene esa sobrecarga. Ambos sufren el problema de que los valores fuera de rango resultan en un comportamiento indefinido.

Debe usar las funciones strtol() o strtoul(), que proporcionan una detección y comprobación de errores mucho mejores. También te dejan saber si toda la cadena fue consumida.

Si desea una int, siempre se puede utilizar strtol(), y después comprobar el valor devuelto para ver si se encuentra entre INT_MIN y INT_MAX.

+0

como una bonificación adicional para 'strtol', etc. si configura su * base * a' 0', obtiene la elección automática de la conversión de entrada octal, decimal o hexadecimal. –

+0

Una posible preocupación al usar base 0 es que las cadenas que comienzan con '0' se interpretarán como base 8 (octal). Se espera este comportamiento con usuarios conocedores, pero demasiadas personas no son conscientes de esto y se sorprenden al encontrar que '012' se convierte en 10 y '019' se convierte en 1 cuando se detuvo la conversión debido al dígito no octal 9. – chux

0

Si el usuario entra 34abc y se pasa a Atoi volverá 34. Si desea validar el valor introducido a continuación, usted tiene que utilizar isdigit en la cadena introducida de forma iterativa

4

Para @r .. creo que no es suficiente para verificar errno para detección de errores en la llamada strtol.

long strtol (const char *String, char **EndPointer, int Base) 

También deberá comprobar EndPointer para ver si hay errores.

2

Combinando R .. y Pickboy respuestas para abreviar

long strtol (const char *String, char **EndPointer, int Base) 

// examples 
strtol(s, NULL, 10); 
strtol(s, &s, 10); 
2

Cuando no hay preocupación por los problemas de entrada de cadena o un área válida, utilizan la más simple: atoi()

De lo contrario, el método con el mejor de error/la detección de rango no es atoi(), ni sscanf(). This good answer todos los detalles listos la falta de comprobación de error con atoi() y algunos error comprobación con sscanf().

strtol() es la función más estricta en la conversión de una cadena a int. Sin embargo, es solo un comienzo. A continuación hay ejemplos detallados para mostrar el uso correcto y, por lo tanto, el motivo de esta respuesta después del accepted one.

// Over-simplified use 
int strtoi(const char *nptr) { 
    int i = (int) strtol(nptr, (char **)NULL, 10); 
    return i; 
} 

Este es el como atoi() y descuida utilizar las características de detección de error de strtol().

Para utilizar plenamente strtol(), hay varias características a considerar:

  1. Detección de ninguna conversión: Ejemplos: "xyz", o "" o "--0"? En estos casos, endptr coincidirá con nptr.

    char *endptr; 
    int i = (int)strtol(nptr, &endptr, 10); 
    if (nptr == endptr) return FAIL_NO_CONVERT; 
    
  2. caso de toda la cadena de convertir o sólo la parte anterior: ¿Es "123xyz" OK?

    char *endptr; 
    int i = (int)strtol(nptr, &endptr, 10); 
    if (*endptr != '\0') return FAIL_EXTRA_JUNK; 
    
  3. detectar si el valor era tan grande, el resultado no es representable como long como "999999999999999999999999999999".

    errno = 0; 
    long L = strtol(nptr, &endptr, 10); 
    if (errno == ERANGE) return FAIL_OVERFLOW; 
    
  4. detectar si el valor estaba fuera del rango de que int, pero no long. Si int y long tienen el mismo rango, esta prueba no es necesaria.

    long L = strtol(nptr, &endptr, 10); 
    if (L < INT_MIN || L > INT_MAX) return FAIL_INT_OVERFLOW; 
    
  5. Algunas implementaciones van más allá de la norma C y establecen errno por razones adicionales, como errno to EINVAL in case no conversion was performed o EINVAL The value of the Base parameter is not valid.. El mejor momento para probar estos valores errno depende de la implementación.

Poniendo todo esto junto: (Ajustar a sus necesidades)

#include <errno.h> 
#include <stdlib.h> 

int strtoi(const char *nptr, int *error_code) { 
    char *endptr; 
    errno = 0; 
    long i = strtol(nptr, &endptr, 10); 

    #if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX 
    if (errno == ERANGE || i > INT_MAX || i < INT_MIN) { 
    errno = ERANGE; 
    i = i > 0 : INT_MAX : INT_MIN; 
    *error_code = FAIL_INT_OVERFLOW; 
    } 
    #else 
    if (errno == ERANGE) { 
    *error_code = FAIL_OVERFLOW; 
    } 
    #endif 

    else if (endptr == nptr) { 
    *error_code = FAIL_NO_CONVERT; 
    } else if (*endptr != '\0') { 
    *error_code = FAIL_EXTRA_JUNK; 
    } else if (errno) { 
    *error_code = FAIL_IMPLEMENTATION_REASON; 
    } 
    return (int) i; 
} 

Nota: Todas las funciones mencionadas permiten espacios iniciales, una opción que lleva signo carácter y se ven afectados por local cambio. Se requiere un código adicional para una conversión más restrictiva.


Nota: El título no OP cambia el énfasis sesgado. Esta respuesta se aplica mejor al título original "cadena de conversión a entero sscanf o atoi"

Cuestiones relacionadas