2010-03-17 4 views
6

Tengo una función que hace una serie de llamadas a sscanf() y luego, después de cada, actualiza el puntero de cadena para que apunte al primer carácter no consumida por sscanf() así:función de envoltura sscanf para avanzar puntero de cadena en C

if(sscanf(str, "%d%n", &fooInt, &length) != 1) 
{ 
    // error handling 
} 
str+=length; 

con el fin de limpiarlo y evitar la duplicación de esto varias veces, me gustaría resumir esto en una función de utilidad agradable que se ve algo como lo siguiente:

int newSscanf (char ** str, const char * format, ...) 
{ 
    int rv; 
    int length; 
    char buf[MAX_LENGTH]; 
    va_list args; 

    strcpy(buf, format); 
    strcat(buf, "%n"); 
    va_start(args, format); 
    rv = vsscanf(*str, buf, args, &length); //Not valid but this is the spirit 
    va_end(args); 
    *str += length; 

    return rv; 
} 

Entonces podría simplificar las llamadas como abajo para remo ve el parámetro adicional/contabilidad:

if(newSscanf(&str, "%d", &fooInt) != 1) 
{ 
    // error handling 
} 

Por desgracia, no puedo encontrar una manera de añadir el parámetro &length al final de la lista arg directamente o no dentro newSscanf(). ¿Hay alguna manera de solucionar este problema, o estoy tan bien manejando la contabilidad a mano en cada llamada?

+0

Tendrá que darse por vencido en sscanf(). Escriba analizadores dedicados que analicen un tipo específico. strtod, strtol, strtok son tus armas básicas. –

Respuesta

3

tienes razón - no se puede meter parámetros adicionales en un va_list. Lo mejor que puede hacer es probablemente algunos trucos macro como esta:

int _newSscanf (char ** str, int *length, const char * format, ...) 
{ 
    int rv; 
    va_list args; 

    va_start(args, format); 
    rv = vsscanf(*str, format, args); 
    va_end(args); 
    *str += *length; 

    return rv; 
} 

#define NEW_SSCANF_INIT int _ss_len 
#define newSscanf(str, fmt, ...) _newSscanf(str, &_ss_len, fmt "%n", __VA_ARGS__, &_ss_len) 

... y requieren de la persona que llama a hacer:

NEW_SSCANF_INIT; 

if (newSscanf(&str, "%d", &fooInt) != 1) 
{ 
    // error handling 
} 

Si puede utilizar extensiones del CCG, puede utilizar "expresiones de los estados "para eliminar la pieza NEW_SSCANF_INIT, haciéndola más limpia:

#define newSscanf(str, fmt, ...) ({int _ss_len; _newSscanf(str, &_ss_len, fmt "%n", __VA_ARGS__, &_ss_len);}) 
+0

. Aparte de reemplazar 'buf' con' format' en _newSscanf funciona muy bien como está escrito, ¡gracias! – Dusty

+0

Solucionado para la posteridad (y he agregado una mejora si las extensiones de GCC están disponibles para usted). – caf

1

Si no se explica cómo funcionan las listas variadas bajo las carátulas (y, por lo tanto, no permite que el código sea portátil), no hay forma de modificar los argumentos.

Pero tuve un pensamiento que puede o no funcionar. No lo he probado, ya que realmente no creo que debas usarlo, pero si estás empeñado en hacerlo, puede ser útil.

Como lo que desea es obtener el número de caracteres escaneados, debe saber que no tiene que hacerlo al mismo tiempo que la configuración real de las variables de la persona que llama.

Haga que su código explore la cadena para establecer los argumentos según lo necesite la persona que llama. No se necesita ningún cambio en absoluto.

El próximo paso es el poco complicado.

contar el número de % caracteres en la cadena de formato que no son inmediatamente seguido por % o * - en otras palabras, el número de variables que necesitan ser suministrada a la sscanf. Afirme si esto es mayor que su límite superior (vea el código a continuación).

Luego agregue un %n secuencias al final de su cadena de formato para asegurarse de que obtendrá el recuento de caracteres.

Luego, utilizando su nueva cadena de formato, utilice un búfer no deseado (en varias ocasiones) para recibir todos los valores del análisis, incluido el último (cantidad de caracteres).

Algo como esto (la responsabilidad de la depuración es el suyo):

typedef union { 
    char junk[512]; // Be *very* careful with "%s" buffer overflows. 
    int length; 
} tJunkbuff; 

int newSscanf (char **str, const char *format, ...) { 
    int rv, length; 
    char buf[MAX_LENGTH]; 
    tJunkBuff junkbuff; 
    va_list args; 

    // Populate variables. 

    va_start (args, format); 
    rv = vsscanf (*str, buf, args); 
    va_end (args); 

    // Get length. 

    // String scanning for % count and assert/error left out. 
    // Only 20 allowed (depends on number of jb.junk variables below (n-1)). 
    strcpy (buf, format); 
    strcat (buf, "%n"); 
    sscanf (*str, buf, 
     jb.junk,jb.junk, jb.junk, jb.junk, jb.junk, 
     jb.junk,jb.junk, jb.junk, jb.junk, jb.junk, 
     jb.junk,jb.junk, jb.junk, jb.junk, jb.junk, 
     jb.junk,jb.junk, jb.junk, jb.junk, jb.junk, 
     jb.junk); // May need to be "&(jb.junk)" ? 
    *str += jb.length; 

    return rv; 
} 

estaría interesado en escuchar lo que pasa si decide darle un tiro. Ese es mi trabajo (y responsabilidad) hecho.Me complace venderte la motosierra, pero si te cortas la pierna mientras la usas, ese es tu problema :-)

+0

Sí, creo que es demasiado pirateo para usar en un sistema de producción, pero +1 para creatividad y para abusar de uniones = D – Dusty

+0

Tendría que ser un "char *" real en la unión, apuntando al 'char [512]' buffer. No funciona de manera portable de todos modos, imagine 16 bit 'int' y 32 bit' char * '- luego puede desincronizarse, por lo que escribe el valor de' "% n" 'en el relleno de la unión. – caf

0

Llamas incorrectamente a la función, mira el parámetro char **str que implica una llamada parámetro REFERENCIA:

 
if(newSscanf(&str, "%d", &fooInt) != 1) 
{ 
    // error handling 
} 
+0

Sí, tuve un error tipográfico que ahora está arreglado, gracias. Desafortunadamente, el problema sigue en pie y no puedo agregar algo a la lista va_ – Dusty

Cuestiones relacionadas