web-dev-qa-db-fra.com

Lire une ligne en utilisant scanf () n'est pas bon?

scanf(" %[^\n]",line);

Un de mes amis a suggéré que l'utilisation de fgets() pour lire une ligne en entrée serait une bien meilleure idée que d'utiliser scanf() comme dans la déclaration ci-dessus. Est-il justifié?

15
amulous

char * fgets ( char * str, int num, FILE * stream ); est sûr à utiliser car il évite débordement de tampon problème, il scanne seulement num-1 nombre de caractères.

Lit les caractères du flux et les stocke en tant que chaîne C dans str jusqu'à ce que (num-1) caractères soient lus ou soit une nouvelle ligne ou la fin du fichier soit atteinte, selon la première éventualité.

ici le deuxième argument num est le nombre maximum de caractères à copier dans str (y compris le caractère nul final).

Par exemple, supposons que dans votre code, la capacité d'un tableau de chaînes ne soit que de 5 Caractères comme ci-dessous.

 char str[5];
 fgets (str, 5, fp);  //5 =you have provision to avoid buffer overrun 

En utilisant le code ci-dessus, si l'entrée de fp est plus longue que les caractères 4, fgets() lira juste en premier 4 Caractères, puis ajoutera \0 (, et supprimer les autres caractères d'entrée supplémentaires, stocke simplement cinq caractères dans str[]).

Alors que scanf(" %[^\n]",str); lira jusqu'à ce que \n Ne soit pas trouvé et si la chaîne d'entrée est plus longue, alors 4 Caractères scanf() provoquera débordement de tampon (car scanf tentera d'accéder à la mémoire au-delà de l'index max 4 dans str[]).

21
Grijesh Chauhan

La FAQ C contient des explications détaillées sur le problème de scanf:

Plus généralement, scanf est conçu pour une entrée relativement structurée et formatée (son nom est en fait dérivé de "scan formaté"). Si vous faites attention, il vous dira s'il a réussi ou échoué, mais il ne peut vous dire qu'environ où il a échoué et pas du tout comment ni pourquoi. Vous avez très peu d'occasions de récupérer des erreurs.

voir ici pour plus de détails.

8
Yu Hao

Autrement dit: oui, fgets est un meilleur choix.

J'ai regardé votre spécificateur de format scanf et j'ai été mystifié. Comprendre exactement ce qu'il fait nécessite un certain temps pour lire les pages man.

De plus, votre code scanf est sensible aux dépassements de tampon.

Restez simple et vous réduirez les coûts de maintenance et éviterez les bogues difficiles à trouver!

3
Klas Lindbäck

fgets sera mieux que cela scanf. Il peut y avoir des problèmes avec scanf comme indiqué dans OP

1) débordement de tampon comme suggéré par @Grijesh

2) éventuellement suivant scanf après cela ne fonctionnera pas car la nouvelle ligne est laissée dans le flux d'entrée. (Si vous manquez un espace vide)

3
Dayal rai

Oui, fgets est le moyen le plus sûr et le plus sûr de lire une ligne à partir de l'entrée standard.

De plus il y aura plus de lisibilité dans le code. Regardez la déclaration scanf que vous avez donnée.

Toute deuxième personne qui le verra sera complètement confuse. Mais alors qu'il y aura plus de lisibilité pour les fgets et c'est facile à comprendre.

3
Kranthi Kumar