2008-10-11 13 views
5

tengo la siguiente secuencia de caracteres:cadena de progreso de análisis en C

"..1....10..20....30...40....50...80..." 

y tengo que extraer todos los números de la misma en una matriz.

¿Cuál es la mejor manera de hacerlo en C?

Respuesta

11

Tal vez la forma más fácil es usar la función strtok() (o strtok_r() si reentrada es una preocupación):

char str[] = "..1...10...20"; 
char *p = strtok(str, "."); 
while (p != NULL) { 
    printf("%d\n", atoi(p)); 
    p = strtok(NULL, "."); 
} 

Una vez que tenga los resultados de llamar a atoi(), debería ser una cuestión sencilla de ahorrar esos números enteros en una matriz.

+0

¡Lo intentaré! –

4

Se puede utilizar un código de sscanf con asignación suprimida (% * [.]) Para saltar sobre los puntos (o cualquier otro carácter que desea), y un carácter analizado código de cuenta% n para avanzar el puntero de cadena.

const char *s = "..1....10..20....30...40....50...80..."; 
int num, nc; 

while (sscanf(s, "%*[.]%d%n", &num, &nc) == 1) { 
    printf("%d\n", num); 
    s += nc; 
} 
+0

Para abordar la queja de Robert Gamble de que esto no maneja cadenas sin puntos iniciales, puede optar por escribir esto: while (sscanf (s, "% d% n", & num, & nc) == 1 || sscanf (s, "% * [.]% d% n", & num, & nc) == 1)/* la evaluación de cortocircuito es impresionante */ – ephemient

-2

Prefiero el uso de strtok en un bucle for. Lo hace sentir más natural, aunque la sintaxis se ve un poco rara.

char str[] = "..1....10..20....30...40....50...80..." 
for (char* p = strtok(strtok, "."); p != NULL; p = strtok(NULL, ".")) 
{ 
    printf("%d\n", atoi(p)); 
} 
+0

De hecho, se ve bastante raro. Hubiera esperado algo como (p = strtok (str, ","); p = strtok (NULL, ".");), Un punto y coma después de la primera línea y una declaración de p. – mweerden

+0

Sí, el código está escrito en la parte superior de mi cabeza. Corregido –

+0

No creo que funcione la inicialización en la posición de prueba del ciclo for. Esto se ejecutará cada vez que se inicie el bucle. No creo que eso sea lo que quieres. La versión de mweerden es mejor, pero utilizo una prueba explícita en la posición de prueba y muevo la próxima búsqueda de tokens hasta el final. – tvanfosson

1

Aquí es la forma correcta de hacerlo, es un poco más largo que la forma más sencilla pero no sufre un comportamiento indefinido si el valor leído está fuera de rango, funciona correctamente si el primer carácter es no un punto, etc. No especificó si los números podrían ser negativos, así que usé un tipo con signo pero solo permití valores positivos, puede cambiar esto fácilmente al permitir el signo negativo en la parte superior del ciclo while interno. Esta versión permite que cualquier carácter que no sea un dígito delimite números enteros, si solo quiere que se permitan los puntos, puede modificar el ciclo interno para omitir solo los puntos y luego buscar un dígito.

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

#define ARRAY_SIZE 10 

size_t store_numbers (const char *s, long *array, size_t elems) 
{ 
    /* Scan string s, returning the number of integers found, delimited by 
    * non-digit characters. If array is not null, store the first elems 
    * numbers into the provided array */ 

    long value; 
    char *endptr; 
    size_t index = 0; 

    while (*s) 
    { 
    /* Skip any non-digits, add '-' to support negative numbers */ 
    while (!isdigit(*s) && *s != '\0') 
     s++; 

    /* Try to read a number with strtol, set errno to 0 first as 
    * we need it to detect a range error. */ 
    errno = 0; 
    value = strtol(s, &endptr, 10); 

    if (s == endptr) break; /* Conversion failed, end of input */ 
    if (errno != 0) { /* Error handling for out of range values here */ } 

    /* Store value if array is not null and index is within array bounds */ 
    if (array && index < elems) array[index] = value; 
    index++; 

    /* Update s to point to the first character not processed by strtol */ 
    s = endptr; 
    } 

    /* Return the number of numbers found which may be more than were stored */ 
    return index; 
} 

void print_numbers (const long *a, size_t elems) 
{ 
    size_t idx; 
    for (idx = 0; idx < elems; idx++) printf("%ld\n", a[idx]); 
    return; 
} 

int main (void) 
{ 
    size_t found, stored; 
    long numbers[ARRAY_SIZE]; 
    found = store_numbers("..1....10..20....30...40....50...80...", numbers, ARRAY_SIZE); 

    if (found > ARRAY_SIZE) 
    stored = ARRAY_SIZE; 
    else 
    stored = found; 

    printf("Found %zu numbers, stored %zu numbers:\n", found, stored); 
    print_numbers(numbers, stored); 

    return 0; 
} 
+0

En primer lugar, la respuesta simple no se rompe si no hay puntos iniciales y, por definición, dado que se trata de un indicador de progreso, la entrada está bien delimitada en términos de rango numérico. Usar strtol() o strtoul() es probablemente una mejora sobre atoi(), pero su solución es innecesariamente compleja. –

+0

La versión de strtok no funciona en literales de cadena, no almacena los números en una matriz y utiliza la atoi problemática. La solución sscanf no funciona sin puntos principales y tiene problemas similares a atoi. Mi solución es más larga, pero también es un programa completo con comprobación de errores y comentarios. –