2016-07-17 4 views
1

Je lis l'entrée de l'utilisateur à partir de la ligne de commande, vérifie s'il s'agit d'un chemin de fichier valide et, si ce n'est pas le cas, demande à l'utilisateur de réessayer. Dans le cas où l'entrée de l'utilisateur est nil, il doit être traité comme toute autre entrée incorrecte la première fois, en laissant l'utilisateur entrer une nouvelle valeur, mais la deuxième fois que nil est entré, le programme devrait forcer quitter.Appeler la ligne readline de Swift à la même variable ignore le deuxième appel

(Je suppose qu'une valeur nil est quelque chose qu'un utilisateur n'entrera pas intentionnellement, donc si cela arrive plus de deux fois je suppose que quelque chose s'est mal passé et quitte le programme pour éviter une boucle sans fin demandant une nouvelle entrée. Cela peut ou peut ne pas être un bon moyen de le faire mais cela n'affecte pas la question.)

Le problème est que readLine() ne demande pas l'entrée de l'utilisateur la deuxième fois il est appelé après s'il a reçu un entrée de fin de ligne (qui donne la valeur nil). (Fin de ligne peut être entrée avec^D.)

Cela signifie que la fonction où readLine() se trouve revient automatiquement nil parce que c'est la dernière valeur de la variable qui reçoit le readLine().

Questions

  1. ne devraient readLine() être appelés, peu importe quelle valeur la variable récepteur a déjà?

  2. Si oui, pourquoi l'utilisateur n'a-t-il pas été invité à entrer nil lorsqu'il a été entré une fois?

Voici le code:

import Foundation 

/** 
    Ask the user to provide a word list file, check if the file exists. If it doesn't exist, ask the user again. 
*/ 
func askUserForWordList() -> String? { 
    print("") 
    print("Please drag a word list file here (or enter its path manually), to use as basis for the statistics:") 

    var path = readLine(stripNewline: true) // THIS IS SKIPPED IF "PATH" IS ALREADY "NIL". 

    return path 
} 

/** 
    Check the user input // PROBABLY NOT RELEVANT FOR THIS QUESTION 
*/ 
func fileExists(filePath: String) -> Bool { 
    let fileManager = NSFileManager.defaultManager() 

    if fileManager.fileExistsAtPath(filePath) { 
     return true 
    } else { 
     return false 
    } 
} 

/** 
    Get the file from the user and make sure it’s valid. 
*/ 
func getFilePathFromUser() throws -> String { 

    enum inputError: ErrorType { 
     case TwoConsecutiveEndOfFiles 
    } 
    var correctFile = false 
    var path: String? = "" 
    var numberOfConsecutiveNilFiles = 0 

    repeat { 
     // Check that the user did not enter a nil-value (end-of-file) – if they did so two times in a row, terminate the program as this might be some kind of error (so that we don't get an infinite loop). 
     if numberOfConsecutiveNilFiles > 1 { // FIXME: entering ^D once is enough to end the program (it should require two ^D). Actually the problem seems to be in function "askUserForWordList()". 
      throw inputError.TwoConsecutiveEndOfFiles 
     } 

     path = askUserForWordList() 

     if path == nil { 
      numberOfConsecutiveNilFiles += 1 
     } else { 
      numberOfConsecutiveNilFiles = 0 

      correctFile = fileExists(path!) 

      if !correctFile { 
       print("") 
       print("Oops, I couldn't recognize that file path. Please try again.") 
      } 
     } 
    } while !correctFile 

    return path! 
} 

// This is where the actual execution starts 

print("") 
print("=== Welcome to \"Word Statistics\", command line version ===") 
print("") 
print("This program will give you some statistics for the list of words you provide.") 
do { 
    let path = try getFilePathFromUser() 
} catch { 
    print("Error: \(error)") 
    exit(-46) // Using closest error type from http://www.swiftview.com/tech/exitcodes.htm (which may not be standard at all. I could, however, not find any "standard" list of exit values). 
} 

Remarques

  • Lorsque vous entrez dans un autre chemin non valide (toute chaîne, même un vide (presser la touche Entrée), la boucle fonctionne comme prévu
  • À l'origine path dans la fonction askUserForWordList() a été déclaré comme consta nt (let path = readLine(stripNewline: true)) mais je l'ai changé en var car il est censé être mis à jour chaque fois que la fonction est appelée. Cependant, cela n'a pas affecté le fonctionnement du programme.
  • J'ai essayé de déclarer path séparément sur la ligne avant d'appeler readLine(), ce qui n'a fait aucune différence.
  • J'ai essayé d'ignorer complètement la variable path et de laisser la fonction askUserForWordList() renvoyer le résultat readLine() directement (return readLine(stripNewline: true)). Cela n'a fait aucune différence.
  • J'ai ignoré la fonction askUserForWordList() tous ensemble et déplacé le code demandant l'entrée de l'utilisateur dans le code "principal" de la fonction "getFilePathFromUser()", mais cela n'a rien changé.

    Code mise à jour:

    func getFilePathFromUser() throws -> String { 
    
        enum inputError: ErrorType { 
         case TwoConsecutiveEndOfFiles 
        } 
        var correctFile = false 
        var path: String? = "" 
        var numberOfConsecutiveNilFiles = 0 
    
        repeat { 
         // Check that the user did not enter a nil-value (end-of-file) – if they did so two times in a row, terminate the program as this might be some kind of error (so that we don't get an infinite loop). 
         if numberOfConsecutiveNilFiles > 1 { // FIXME: entering ^D once is enough to end the program (it should require two ^D). Actually the problem seems to be in function "askUserForWordList()". 
          throw inputError.TwoConsecutiveEndOfFiles 
         } 
    
         // MODIFIED – This code was previously located in "askUserForWordList()" 
         print("") 
         print("Please drag a word list file here (or enter its path manually), to use as basis for the statistics:") 
    
         path = readLine(stripNewline: true) 
    
         // END OF MODIFICATION 
    
         if path == nil { 
          numberOfConsecutiveNilFiles += 1 
         } else { 
          numberOfConsecutiveNilFiles = 0 
    
          correctFile = fileExists(path!) 
    
          if !correctFile { 
           print("") 
           print("Oops, I couldn't recognize that file path. Please try again.") 
          } 
         } 
        } while !correctFile 
    
        return path! 
    } 
    
+1

En ce qui concerne la 'sortie (-46)', les codes de sortie rapide sont les mêmes que dans C, de sorte que les seules valeurs "standard" sont 0 ('EXIT_SUCCESS') et 1 (' EXIT_FAILURE'). Si vous avez l'intention de quitter de toute façon, je suggère d'utiliser 'fatalError()' à la place, car il affiche le fichier et le numéro de ligne du plantage d'origine qui facilite le débogage. – xoudini

Répondre

1

readLine() rendements nil si (et seulement si) le descripteur de fichier d'entrée standard a atteint la fin de fichier. Cela se produit (par exemple) lors de la lecture de l'entrée à partir d'un tty, et Ctrl-D (le "end-of-transmission character") est entré en tant que premier caractère sur une ligne.

Tous les appels readLine() suivants renvoient alors également nil, il n'y a pas de de manière à détecter que "Ctrl-D a été entré deux fois". En d'autres termes, une fois que l'entrée standard est dans la condition de fin de fichier , vous ne pouvez plus en lire aucune donnée. Si vos programmes nécessitent plus de données, vous ne pouvez signaler qu'une erreur, par ex.

guard let path = readLine(stripNewline: true) else { 
    throw InputError.UnexpectedEndOfFile 
}