float - scanf char
Usuario escribiendo terminador nulo en scanf (4)
¿Puede un usuario escribir un terminador nulo en una entrada, para lo cual se usa scanf, por lo que la longitud de la entrada sería 0?
char msg[1000];
scanf("%1000s",msg); // the user would type nothing: '''' or ''/0''
Algunos sistemas permiten que el usuario de la terminal ingrese un byte NUL presionando Ctrl - @ . scanf()
almacenaría este byte en la matriz de destino, pero lo más probable es que no considere que este byte constituya un separador de palabras, por lo que el análisis de entrada continuará hasta que se ingrese el final del archivo o un carácter de espacio en blanco como nueva línea.
No hay forma de saber desde la matriz de destino si se almacenó un byte NUL después de otros caracteres, ni tampoco cuántos caracteres adicionales se han leído después de este byte NUL. Pero si scanf()
devolvió 1
y msg
tiene una longitud cero, la única posibilidad es que el usuario ingrese un byte NUL después de una secuencia posiblemente vacía de caracteres en blanco.
Esta también sería la única posibilidad para que fgets()
devuelva una cadena vacía al pasar una matriz de destino con un tamaño mayor que 1.
Si la entrada se lee de un archivo en lugar de un terminal, siempre existe la posibilidad de que el archivo contenga bytes NUL y se debe tener cuidado para evitar un comportamiento indefinido si ocurre. Por ejemplo, aquí hay un error clásico:
char msg[1000];
if (fgets(msg, sizeof msg, stdin)) {
int len = strlen(msg);
if (msg[len - 1] == ''/n'') { // potential undefined behavior!
msg[--len] = ''/0'' // potential undefined behavior!
}
...
}
También tenga en cuenta que su código tiene un defecto: la cantidad máxima de caracteres para almacenar en el destino debe ser 999
, no 1000
:
char msg[1000];
if (scanf("%999s",msg) == 1) {
/* word was read from the user */
if (*msg == ''/0'') {
printf("user entered a NUL byte/n");
}
}
En muchos sistemas, la respuesta es "Sí".
Por lo general, la secuencia mágica es Control- @ .
El código de carácter para @ es usualmente 64, uno menos el de A (65). Control-A es el código de carácter 1; uno menos es el código de carácter 0, también conocido como ''/0''
.
Tenga en cuenta que para obtener una entrada de longitud cero, deberá escribir el byte nulo como el primer carácter que no sea de espacio, y aún deberá presionar regresar para ingresar la entrada. Su programa encontraría difícil identificar de manera confiable qué más se ingresó después del byte nulo.
Sí, es posible porque ''/0''
es un espacio no blanco. scanf()
considerará que es el final de la cadena. Entonces %s
puede coincidir con una cadena vacía.
Puede usar el especificador m
para asignar un búfer correspondiente para contener la entrada. Tenga en cuenta que este es el estándar POSIX 200112L.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char *str;
int n;
if (scanf("%ms%n", &str, &n) == 1) {
printf("I read %d character(s)./n", n);
if (strlen(str) != 0) {
printf("str = %s/n", str);
}
else {
printf("str is empty");
}
free(str);
}
else {
printf("The user enter nothing");
}
}
¿Puede un usuario escribir un terminador nulo en una entrada, para lo cual se usa scanf, por lo que la longitud de la entrada sería 0?
Además de que un usuario escriba un carácter nulo , stdin
podría usar una entrada redirigida que contenga caracteres nulos.
foo < data.bin
Detalle: cuando el primer carácter escaneado es un carácter nulo o ''/0''
, se lee como cualquier otro carácter y se guarda como cualquier otro carácter que no sea de espacio en blanco. Por lo tanto, la longitud de la entrada es aún mayor que 0, pero el valor informado por strlen(msg)
es engañado por el carácter nulo incorporado y devuelve cero.
fgets()
tiene el mismo problema que scanf()
a este respecto: strlen(msg)
no siempre informa el número de caracteres leídos.
getline()
no estándar getline()
devuelve el número de caracteres leídos y es una forma de salir de esto.
@Stargateur menciona el uso de "%n"
para almacenar la cantidad de caracteres guardados en msg
. Tal vez como
char msg[1000 + 1];
int start, end;
if (1 == scanf(" %n%1000s%n", &start, msg, &end)) {
printf("%d characters read/n", end - start);
}
IAC, estamos tratando de hacer frente a una debilidad de esquina de scanf()
y familia. La mejor solución de entrada de lectura depende de factores adicionales, no mencionados por OP.