Toutes mes excuses C noob ici,
J'essaye de valider l'entrée utilisateur de stdin mais je ne trouve pas un moyen de gérer une seule entrée \ n . Pour simplifier complètement le problème, j'ai écrit ci-dessous. Lorsque \ n est entré, scanf se termine, s'il s'agit du premier caractère entré, par exemple, un utilisateur frappe immédiatement entrer, 6 sera généré par le Instruction printf .
int main(){
char x[6];
scanf("%[^\n]s", x);
printf("%i\n", strlen(x));
return 0;
}
Mon problème est que je ne trouve pas de moyen de savoir si un utilisateur a entré un \ n ou 6 personnages légitimes. Je voudrais également définir une vérification d'entrée MAX et je ne suis pas sûr si scanf est la fonction d'écriture avec laquelle prendre les entrées de l'utilisateur dans ce cas ou fgets peut être mieux adapté.
4 Réponses :
Lorsque \ n est entré scanf se termine, s'il s'agit du premier caractère entré, par exemple Un utilisateur frappe immédiatement enter, 6 sera affiché par l'instruction printf.
Comportement non défini. Rien n'a été lu dans
x []non initialisé, doncstrlen (x)est UB.
scanf ("% [^ \ n] s ", x);n'a pas de protection de largeur. Il n'a aucun contrôle de la valeur de retour. Détecter uniquement un'\ n'n'est pas anodin.Utilisez
fgets().int main(){ // char x[6]; char x[7]; // add at least 1 (useful for the \n code may read) // scanf("%[^\n]s", x); if (fgets(x, sizeof x, stdin)) { x[strcspn(x, "\n")] = '\0'; // lop off potential trailing \n // printf("%i\n", strlen(x)); printf("%zu\n", strlen(x)); // use matching specifier return 0; } }
char *removetrailinglineends(char *str)
{
size_t len = strlen(str);
char *end = str + !len ? str : str + len - 1;
while(end > str)
{
if(*end == '\n' || *end == '\r') *end = 0;
end--;
}
}
Vous pouvez utiliser getchar () pour vérifier chaque caractère d'entrée individuel et arrêter de prendre des entrées lorsque la taille d'entrée est de 6 ou que l'utilisateur saisit \ n .
Modifier: j'ai mis à jour mon code conformément à la suggestion de @ chux. Vous devez augmenter la taille du tableau à 7 afin que le dernier caractère puisse être le terminateur nul lorsque l'entrée dépasse 6 caractères. J'ai également utilisé une variable int c pour stocker les valeurs de retour de getchar au lieu de les stocker directement dans x [i] afin qu'il puisse identifier EOF .
#include<stdio.h>
#include<string.h>
int main(){
char x[7];
int c;
size_t i = 0;
while (i < 6 && (c=getchar()) != EOF) {
x[i] = c;
if (x[i] == '\n') {
break;
}
++i;
}
x[i] = '\0'; // null-terminate string
printf("%s\n", x);
printf("%lu\n", strlen(x));
return 0;
}
J'aime getchar () pour cela, cela vous donne un contrôle total.
@chux Merci pour votre suggestion. J'ai mis à jour ma réponse pour éviter les problèmes que vous avez mentionnés.
Outre la question principale, le code présente plusieurs problèmes:
char x[6] = ""; // fill NUL chars
int n_read = scanf ("%5[^\n]", x); // ok
if (n_read < 0) error...
else if (n_read == 0) newline or EOF...
else /*1*/ x is ready...
Résoudre les problèmes un par un:
char x[6] = ""; // fill NUL chars
scanf ("%5[^\n]", x); // better
Cela se lira jusqu'à la nouvelle ligne et s'arrêtera, mais peut encore déborder de la mémoire tampon:
scanf ("%5[^\n]", x); // better
Cela évitera le débordement de tampon, en limitant la lecture à 5 caractères. Malheureusement, si l'entrée est longue, il n'y a aucune garantie qu'un terminateur nul sera écrit à la fin. De plus, si scanf échoue (comme dans votre cas), alors x ne sera pas initialisé:
scanf("%[^\n]", x); // still not good
Cela fonctionnera mal si après la lecture de 3 caractères, par exemple, il y aura un IO problème. Pour lutter contre ce genre de problèmes, vérifiez le code de retour:
int main(){
char x[6];
// 1. an attack vector: lines with more than 6 characters will exceed buffer size
// 2. The s will never be matched,
// since %[^\n] consumes everything until EOF or \n
// the following char can't be 's'
scanf("%[^\n]s", x);
// if scanf fails, then x can be uninitialized:
printf("%i\n", strlen(x));
return 0;
}
Notez qu'au lieu d'initialiser x = "" , il est probablement préférable de set x [5] = '\ 0' après scanf. J'ai choisi l'option d'initialisation à la place, car il était plus facile de présenter la réponse progressivement de cette façon.
concernant: // si scanf échoue, alors x peut être non initialisé: Ce n'est pas vrai, le premier caractère du tampon 'x []' sera '\ 0'.
@ user3629249 vous vous trompez. Regardez wandbox.org/permlink/E9hs0afnLJJ9rRgW
C'est pourquoi j'ai déclaré, dans mes commentaires, que la valeur renvoyée par scanf () doit être vérifiée. L'exemple référencé ne vérifie pas correctement la valeur renvoyée. Par conséquent, l'exemple référencé est «un très mauvais exemple»
@ user3629249 et je l'ai abordé dans ma réponse
Utilisez fgets
Notez que
"% [^ \ n] s"n'est pas un spécificateur de format valide. Vous lisez une chaîne avec"% [^ \ n]"ou avec"% s"mais la vôtre est un hybride.@WeatherVane C'est valide; c'est le spécificateur
% [suivi de la correspondance d'uns@MM le
sne fait pas partie du spécificateur de format% []et je suis convaincu que l'intention n'est pas de correspondre à un's'. C'est une erreur fréquente.Ne le fais pas. En tant que "C noob" autoproclamé, vous devriez sérieusement envisager de ne pas utiliser du tout scanf. Déjà. Il est difficile à comprendre pour les débutants et à bien utiliser. Vous passerez plus de temps à apprendre les faiblesses du langage scanf qu'à apprendre C. Vous en apprendrez beaucoup plus si vous utilisez simplement
freadoufgetset analysez les données vous-même.scanf a peut-être servi un objectif il y a 30 ans, mais l'OMI n'a plus de raison d'exister. Si vous avez une application où scanf pourrait être utilisé, vous ne devriez probablement pas l'écrire du tout en C.
concernant:
scanf ("% [^ \ n] s", x);1) toujours vérifier la valeur retournée (pas les valeurs de paramètre) pour déterminer si l'opération a réussi. La famille de fonctionsscanf ()renvoie le nombre d'opérations réussies de 'spécificateur de format d'entrée' 2) lors de l'utilisation de '% s' et / ou '% [...]' incluent toujours un modificateur MAX CHARACTERS soit 1 de moins que la longueur du tampon d'entrée. En effet, ces spécificateurs de format ajoutent toujours un octet NUL à l'entrée. Cela évite également toute possibilité de débordement de tampon et le comportement indéfini résultantRemarque: «x» ne comporte que 6 caractères, le nombre maximum de caractères que l'utilisateur peut saisir (sans dépassement de mémoire tampon) est donc de 5 caractères. L'instruction
scanf ()doit être:if (scanf ("% 5 [^ \ n]", x)! = 1) {handle error}Une erreur possible est le premier caractère de l'utilisateur est un '\ n'concernant:
printf ("% i \ n", strlen (x));si l'utilisateur a seulement entré un '\ n' Alors cette instruction affichera0. Bien sûr, le code aurait dû vérifier la valeur renvoyée parscanf ()et ne jamais se soucier d'appeler cette instruction