2012-02-26 8 views
8

En primer lugar, otras preguntas sobre el uso de sscanf no responden a mi pregunta porque la respuesta común es no utilizar sscanf en absoluto y utilizar fgets o getch lugar, lo cual es imposible en mi caso.Cómo usar sscanf correcta y segura

El problema es mi profesor C quiere que yo utilizo scanf en un programa. Es un requisito. Sin embargo, el programa también debe manejar todas las entradas incorrectas.

El programa debe leer una matriz de enteros. No importa en qué formato se suministran los enteros para la matriz. Para facilitar la tarea, el programa podría leer primero el tamaño de la matriz y luego los enteros, cada uno en una nueva línea.

El programa debe manejar las entradas como estos (y reportar errores apropiada):

  1. 999999999999999 ... 9 (números más grande que entero)
  2. 12A3 (no leer esto como un entero de 12)
  3. a ... z (cadenas)
  4. 11 AA 22 33 \ n todas en una línea (esto podría ser manejado por descartando todo después de 11)
  5. entradas más grande que la matriz de entrada

Puede haber casos más incorrectas, estos son los pocos que podía pensar.

Si se suministra la entrada errónea, el programa debe pedir al usuario que introduzca de nuevo hasta la entrada correcta se da, pero la entrada correcta anterior se debe mantener (sólo es incorrecto de entrada debe ser eliminado del flujo de entrada).

Todo debe cumplir con la norma C99.

Respuesta

10

El scanf familia de funciones no se pueden utilizar de manera segura, especialmente cuando se trata de números enteros. El primer caso que mencionas es particularmente problemático. La norma dice esto:

Si este objeto no tiene un tipo apropiado, o si el resultado de la conversión no se puede representar en el objeto, el comportamiento es fi nida unde.

Así de simple. Puede pensar en los trucos %5d, pero encontrará que no son confiables. O tal vez alguien piense en errno. Las funciones scanfno están obligados a establecer errno.

Sigue esto fun little page: terminan abandonando scanf en total.


Así que ir de nuevo a su profesor de C y preguntarle esto: ¿cómo funciona exactamente mandato C99 que sscanf informará de errores?

+1

Técnicamente, puede, siempre que cumpla con los requisitos que se muestran arriba. escanea el tamaño fijo en una cadena, que está bien definida, luego compruébalo y vuelve a escanear para hacer la conversión (que ahora es válida). Un poco ridículo, pero cumple con el requisito de usar scanf sin romperse; – davenpcj

1

Bueno, vamos sscanf aceptan todas las entradas (es decir, como cuerdas)% s programa y luego analizarlos

+0

Normal '% s' en' scanf' === potencial desbordamiento de búfer. – ThiefMaster

+2

@ThiefMaster: Sí, pero no necesariamente en 'sscanf', siempre que el destino sea más grande que la fuente. Por otro lado, '% s' te da palabras delimitadas por espacios en blanco, no toda la cadena. –

+0

@ThiefMaster El uso de un ancho de campo como "% 100s" (por ejemplo, límite de 100 caracteres) se puede utilizar para evitar el desbordamiento. – davenpcj

1

Si tiene que usar scanf para aceptar la entrada, creo que empezar con algo un poco como el siguiente.

int array[MAX]; 
int i, n; 
scanf("%d", &n); 
for (i = 0; i < n && !feof(stdin); i++) { 
    scanf("%d", &array[i]); 
} 

Esto manejar (más o menos), el problema de entrada de formato libre desde scanf se saltará automáticamente líder espacios en blanco cuando se compara un formato %d.

La observación clave para muchas de sus preocupaciones es que scanf le indica cuántos códigos de formato ha analizado correctamente. Así,

int matches = scanf("%d", &array[i]); 
if (matches == 0) { 
    /* no integer in the input stream */ 
} 

creo que este maneja directamente refiere a (3) y (4)

Por sí mismo, esto no es manejar el caso de la entrada 12a3. La primera vez a través del ciclo, scanf sería analizar '12 as an integer 12, leaving the remaining a3` para el siguiente ciclo. Sin embargo, obtendrías un error la próxima vez. ¿Es eso lo suficientemente bueno para los propósitos de tu profesor?

Para números enteros mayores que MAXINT, por ejemplo, "999999 ....... 999", no estoy seguro de lo que puede hacer con recta scanf.

Para entradas más grande que la matriz de entrada, esto no es un problema scanfper se. Solo necesita contar cuántos enteros ha analizado hasta ahora.

Si está permitido el uso de sscanf para decodificar las cadenas después de que han sido extraídos del flujo de entrada mediante algo así como scanf("%s") también se podría hacer algo como esto:

while (...) { 
    scanf("%s", buf); 
    /* use strtol or sscanf if you really have to */ 
} 

Esto funciona para cualquier secuencia de palabras separadas en espacios en blanco, y le permite separar el escaneo de la entrada de palabras, y luego ver si esas palabras parecen números o no. Y, si es necesario, puede usar las variantes scanf para cada parte.

+0

¿Qué hay de usar scanf ('% c', c) en lugar de getchar()? Eso sería seguro y cumpliría el requisito, ¿no? – evodevo

+0

Es cierto, pero 'scanf ("% c ", & c)' es una forma costosa de emular 'getchar()'. ¿La asignación es realmente sobre este tipo de uso sintáctico trivial de 'scanf'? Creo que el comentario de @cnicutar realmente da con un tema central: dado el extracto citado del estándar, es imposible usar 'scanf' de forma segura para analizar las entradas desbordadas del interger. –

+0

@evodevo: 'scanf ('% c', c)' es un buen ejemplo de por qué las cadenas de formato deberían, en general, evitarse en C++. Lástima que uno no puede marcar los comentarios: D –

Cuestiones relacionadas