En su libro The Practice of Programming (que vale la pena leer), Kernighan y Pike discuten este problema y lo resuelven utilizando snprintf()
para crear la cadena con el tamaño de búfer correcto para pasar a la scanf()
familia de funciones. En efecto:
int scanner(const char *data, char *buffer, size_t buflen)
{
char format[32];
if (buflen == 0)
return 0;
snprintf(format, sizeof(format), "%%%ds", (int)(buflen-1));
return sscanf(data, format, buffer);
}
Tenga en cuenta que esto aún limita la entrada al tamaño proporcionado como 'búfer'. Si necesita más espacio, debe realizar la asignación de memoria o utilizar una función de biblioteca no estándar que realice la asignación de memoria por usted.
Tenga en cuenta que la versión POSIX 2008 (2013) de la scanf()
familia de funciones admite un modificador de formato m
(un carácter de asignación asignación) para las entradas de cadena ( %s
, %c
, %[
). En lugar de tomar un char *
argumento, toma un char **
argumento y asigna el espacio necesario para el valor que lee:
char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
printf("String is: <<%s>>\n", buffer);
free(buffer);
}
Si la sscanf()
función no satisface todas las especificaciones de conversión, toda la memoria que asignó para %ms
conversiones similares se libera antes de que la función regrese.
buflen-1
- Gracias. Luego debe preocuparse por el subdesbordamiento sin firmar (envolviendo a un número bastante grande), de ahí laif
prueba. Me sentiría muy tentado a reemplazar eso con anassert()
, o respaldarlo con unassert()
antes deif
que se active durante el desarrollo si alguien es lo suficientemente descuidado como para pasar 0 como tamaño. No he revisado cuidadosamente la documentación para saber qué%0s
significasscanf()
: la prueba podría ser mejor comoif (buflen < 2)
.