2010-10-20 14 views
6

Tengo un archivo de texto con el siguiente contenido:El uso de [] en fscanf()

"abc","def","ghi" 

las siguientes obras para leer el contenido del archivo correctamente:

int main() 
{ 
    char name[1024] = {0}; 
    FILE *file = fopen("file.txt", "r"); 

    while(1) 
    { 
     if (fscanf(file, " %[\",]s ", name) == EOF) 
      break; 
     if (fscanf(file, " %[a-zA-Z]s ", name) == EOF) 
      break; 

     printf("%s\n", name); 
    } 

    return 0; 
} 

Sin embargo, la siguiente falla :

int main() 
{ 
    char name[1024] = {0}, garbage[5]; 
    FILE *file = fopen("file.txt", "r"); 

    while(1) 
    { 
     if (fscanf(file, " %[\",]s%[a-zA-Z]s ", garbage, name) == EOF) 
      break; 

     printf("%s\n", name); 
    } 

    return 0; 
} 

estoy usando MSVC++ 08. ¿Qué me falta? Estoy buscando una solución con fscanf() solo en el while loop.

+2

Mientras que hay un montón de sugerencias que permitan solucionar el problema, la mejor solución es no utilizar 'fscanf' para este tipo de cosas. Obtendrá una solución mucho más robusta (es decir, tratará casos de bordes extraños y entradas mal formadas) si escribe su propia máquina de estado lexer. –

+0

+1 Oli Charlesworth :) – pmg

+0

@Oli: estoy de acuerdo. Estaba resolviendo un problema de programación en el que no tenía que lidiar con entradas raras. para los casos de entrada totalmente válidos, me encantaría hacerlo en la máquina, en lugar de hacerlo manualmente. – Donotalo

Respuesta

7

Funciona ??? pura mala suerte :-)

Sus especificaciones de conversión significan

" %[\",]s " 
     ^= optionally skip whitespace 
     ^== read a literal 's' 
    ^^^^^^=== read an unlimited string of quotes and commas 
^========= optionally skip whitespace 

y

" %[a-zA-Z]s " 
      ^= optionally skip whitespace 
      ^== read a literal 's' 
    ^^^^^^^^^=== read an unlimited string of letters 
^============ optionally skip whitespace 

y

" %[\",]s%[a-zA-Z]s " 
        ^= optionally skip whitespace 
        ^== read a literal 's' 
     ^^^^^^^^^=== read an unlimited string of letters 
     ^============ read a literal 's' 
    ^^^^^^============= read an unlimited string of quotes and commas 
^=================== optionally skip whitespace 

Creo que quieren

" %4[\",]%1023[a-zA-Z] " 
         ^= optionally skip whitespace 
     ^^^^^^^^^^^^^== read a string of at most 1023 letters 
    ^^^^^^^=============== read a string of at most 4 quotes and commas 
^====================== optionally skip whitespace 

Aparte de eso, scanf devuelve el número de conversiones exitosas o EOF en caso de error. Está comparando el valor del resultado con EOF cuando debe comparar con 1 (o 2, o lo que sea): compare con el número de conversiones que espera.

if (scanf() == 3) /* expected 3 conversions */ 
{ /* ok */ } 
else { /* oops, something went wrong */ } 
+0

pensé que la 's' es de 'cadena'. :(en realidad me olvidé totalmente de cómo [] funciona. No pude encontrarlo usando Google. Y por pura mala suerte;) ¡funcionó! – Donotalo

0

la siguiente debería funcionar:

 if (fscanf(file, " %[\",]%[a-zA-Z] ", garbage, name) == EOF) 
     break; 
+0

produce * ghi * dos veces. – Donotalo

2

Retire la "s" bandera de conversión, como en:

if (fscanf(file, " %[\",]%[a-zA-Z] ", garbage, name) < 2) 

Tenga en cuenta que tengo que comparar a 2 en lugar de EOF desde la última comillas se leerá en la próxima iteración.

EDIT: Me sorprende que su primer ejemplo de código también funcione, pero funcionó perfectamente con gcc en Mac OS X, por lo que este no es un problema específico de Microsoft.

4
  1. Los corchetes son su propio especificador de conversión, no un modificador. %[a-zA-Z]s significa "coincide con cualquier número de caracteres alfabéticos y asignar a name, a continuación, coincide un literal s Retire los s caracteres
  2. Si desea hacer coincidir algo, pero descartarlo, utiliza asterisco y no una memoria intermedia de basura:... %*[\",]
  3. scanf si al menos un especificador coincide antes de que el archivo termine. Esto significa que obtendrá un bucle erróneo cuando el puntero del archivo esté detrás del i. Considere probar el recuento de especificadores asignados u otro "%*[\"]" en el final para sorber las cotizaciones finales.

Esta es también la razón por la cual funcionó la primera versión. El literal s no pudo coincidir, pero la primera conversión tuvo éxito, por lo que obtuvo name pero no EOF.


if (fscanf(file, " %*[\",]%[a-zA-Z] ", name) < 1) 
    break; 

o

fscanf(file, " %*[\",]%[a-zA-Z]%*[\"] ", name) 
+0

es genial aprender sobre '% * [...]'. Gracias :) – Donotalo