2010-01-17 16 views
3

Pourquoi «exactement» ce code boucle-t-il indéfiniment si vous entrez un caractère non numérique?Questions sur la validation de la saisie utilisateur en Objective-C, numéro vs chaîne

La première question vient parce que je veux apprendre le bon codage défensif. Est-ce que quelqu'un sait un bon moyen de vérifier la saisie des utilisateurs? Mon google-fu m'a échoué. Certaines personnes semblaient être d'avis que si je spécifie %f dans scanf que je suis 'exigeant' un ; J'ai vérifié cela, en quelque sorte, en imprimant la valeur de userInput. En fait, si je commente la boucle do while, il n'y a pas de problème avec l'exécution du code. Il attribue un 0 à userInput et poursuit ses activités.

#import <Foundation/Foundation.h> 

int main (int argc, const char * argv[]) { 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    float userInput; 
    float result; 

    NSLog(@"3X^3 -5x^2 + 6"); 

    do { 
     NSLog(@"What is x?"); 
     scanf("%f", &userInput); 
     NSLog(@"userInput = %f", userInput); 
    } while(userInput == 0); 

    result = 3 * (userInput * userInput * userInput) - 5 * (userInput * userInput) + 6; 
    NSLog(@"the result is: %f", result); 

    [pool drain]; 
    return 0; 
} 
+0

Ajouter une coche à la réponse de gavinb, si c'était "génial". De cette façon, les futurs intervenants seront encouragés à répondre à vos questions. –

+0

merci, j'essayais de comprendre comment faire cela. – griotspeak

Répondre

3

Cela n'a rien à voir avec Objective-C ou Cocoa. Le problème est simplement lié à l'utilisation de la fonction de bibliothèque C standard scanf et à la gestion de la condition d'erreur. À partir de la page de manuel scanf, décrivant le code retour:

Zéro indique que, bien qu'il y ait eu des entrées disponibles, aucune conversion n'a été affectée; généralement cela est dû à un caractère d'entrée invalide, tel qu'un caractère alphabétique pour une conversion `% d '.

Une entrée numérique valide peut être analysé par scanf avec le prescripteur %f, de sorte que fonctionne évidemment comme prévu. Mais si vous entrez un caractère non numérique, scanf ne peut pas convertir cela en un flottant, et laisse le texte dans le tampon de stdin. Puisque le code ne vérifie pas le code retour de scanf, et que tester seulement si userInput est différent de zéro, la boucle ne se terminera jamais, car userInput arrivera à 0.0, et ne sera jamais mis à jour car scanf ne tirera pas le code non numérique caractères hors du tampon stdin. C'est pourquoi votre code s'exécute dans une boucle infinie.

Si vous aviez initialisé userInput à une valeur non nulle, qui réglerait le problème d'une façon, comme entrée non numérique causerait scanf à l'échec et la condition while serait déclenchée. Mais une meilleure solution serait de vérifier le code retour de scanf. Si elle est égale à zéro, imprimer un message d'erreur, et faire un fpurge(stdin) pour effacer l'entrée invalide avant boucle autour de nouveau, comme ceci:

int rc = scanf("%f", &userInput); 
if (rc == 0) 
{ 
    NSLog(@"Invalid input, try again."); 
    fpurge(stdin); 
} 

C'est donc l'approche C ordinaire à l'entrée et l'analyse syntaxique. La ligne de fond pour le codage défensif est que vous devriez toujours vérifier le code de retour! Comme Chris mentionne, pour une application Cocoa réelle, vous voulez regarder NSNumberFormatter et autres, mais alors vous seriez probablement prendre des entrées de widgets plutôt que des flux de fichiers, de sorte que le code serait très différent de ce qui précède .

+0

Merci! cette explication est géniale. Je pensais que la vérification de userInput était suffisante pour vérifier, mais cela clarifie très bien. merci. – griotspeak

1

La bonne façon de valider les entrées utilisateur à Cocoa est d'utiliser une instance d'une sous-classe appropriée de NSFormatter, dans ce cas quelque chose comme NSNumberFormatter.

+0

alors dois-je créer un NSString et le passer au formateur (numberFromString)? – griotspeak

+0

Gardez à l'esprit que l'utilisateur saisit déjà une chaîne. Ce que vous demandez est fondamentalement "Devrais-je obtenir une chaîne de l'utilisateur, utiliser C pour le transformer en un nombre, le transformer en une chaîne, et le passer à mon formateur?" La réponse à cela est non. Au lieu de cela, obtenez une chaîne de l'utilisateur, passez-la à votre formateur et récupérez un numéro. –