web-dev-qa-db-fra.com

Quelle est la meilleure façon d'obtenir des informations de l'utilisateur en C?

Beaucoup de gens ont dit que scanf ne devrait pas être utilisé dans un "programme plus sérieux", comme avec getline.

J'ai commencé à me perdre: si chaque fonction d'entrée que j'ai rencontrée me disait que je ne devais en utiliser aucune, que devrais-je utiliser? Existe-t-il un moyen plus "standard" d'obtenir des informations dont je ne suis pas au courant?

38
user1115057

Généralement, fgets() est considéré comme une bonne option. Il lit des lignes entières dans un tampon, et à partir de là, vous pouvez faire ce dont vous avez besoin. Si vous voulez un comportement comme scanf(), vous pouvez passer les chaînes que vous lisez à sscanf().

Le principal avantage de cela, c'est que si la chaîne ne parvient pas à se convertir, il est facile de récupérer, alors qu'avec scanf() il vous reste une entrée sur stdin que vous devez drainer. De plus, vous ne vous retrouverez pas dans l'écueil de mélanger les entrées orientées ligne avec scanf(), ce qui provoque des maux de tête lorsque des choses comme \n Restent sur stdin menant couramment de nouvelles codeurs de croire que les appels d'entrée avaient été complètement ignorés.

Quelque chose comme ça pourrait vous plaire:

char line[256];
int i;
if (fgets(line, sizeof(line), stdin)) {
    if (1 == sscanf(line, "%d", &i)) {
        /* i can be safely used */
    }
}

Ci-dessus, vous devez noter que fgets() renvoie NULL sur EOF ou erreur, c'est pourquoi je l'ai enveloppé dans un if. Le sscanf() call renvoie le nombre de champs qui ont été convertis avec succès.

Gardez à l'esprit que fgets() peut ne pas lire une ligne entière si la ligne est plus grande que votre tampon, ce qui dans un programme "sérieux" est certainement quelque chose que vous devriez considérer.

35
FatalError

Pour une entrée simple où vous pouvez définir une limite fixe sur la longueur d'entrée, je vous recommande de lire les données du terminal avec fgets().

En effet, fgets() vous permet de spécifier la taille de la mémoire tampon (par opposition à gets(), qui pour cette raison devrait à peu près jamais être utilisé pour lire les entrées de humains):

char line[256];

if(fgets(line, sizeof line, stdin) != NULL)
{
  /* Now inspect and further parse the string in line. */
}

N'oubliez pas qu'il conservera par exemple le ou les caractères de saut de ligne, ce qui peut surprendre.

UPDATE : Comme indiqué dans un commentaire, il existe une meilleure alternative si vous êtes d'accord pour obtenir la responsabilité du suivi de la mémoire: getline() . C'est probablement la meilleure solution à usage général pour le code POSIX, car elle n'a pas de limite statique sur la longueur des lignes à lire.

11
unwind

L'utilisation de scanf pose plusieurs problèmes:

  • la lecture de texte avec un spécificateur de conversion simple %s présente le même risque que l'utilisation de gets(); si l'utilisateur tape une chaîne plus longue que la taille du tampon cible, vous obtiendrez un dépassement de tampon;

  • si vous utilisez %d ou %f pour lire une entrée numérique, certains mauvais schémas ne peuvent pas être détectés et rejetés complètement - si vous lisez un entier avec %d et les types d'utilisateurs " 12r4 ", scanf convertira et assignera le 12 Tout en laissant r4 Dans le flux d'entrée pour salir la lecture suivante;

  • certains spécificateurs de conversion ignorent les espaces blancs de tête, d'autres non, et le fait de ne pas en tenir compte peut entraîner des problèmes où certaines entrées sont complètement ignorées;

Fondamentalement, cela prend beaucoup d'efforts supplémentaires pour les lectures pare-balles en utilisant scanf.

Une bonne alternative consiste à lire toutes les entrées sous forme de texte à l'aide de fgets(), puis à les convertir en jetons et à convertir en utilisant sscanf ou des combinaisons de strtok, strtol, strtod, etc.

5
John Bode

Utilisez fgets pour obtenir les données et utilisez sscanf (ou une autre méthode) pour les interpréter.

Consultez cette page pour savoir pourquoi il vaut mieux utiliser fgets + sscanf plutôt que scanf

http://c-faq.com/stdio/scanfprobs.html

2
ouah