2016-07-17 2 views
1

Я читаю пользовательский ввод из командной строки, проверяя, является ли он допустимым файловым путем, и если он не просит пользователя повторить попытку.Вызов строки чтения Swift для одной и той же переменной дважды игнорирует второй вызов

В случае ввода пользователем nil его следует рассматривать как любой другой неправильный ввод в первый раз, позволяя пользователю вводить новое значение, но во второй раз, когда вводится nil, программа должна принудительно завершить работу.

(Я предполагаю, что значение nil - это то, что пользователь не будет вводить нарочно, поэтому, если это произойдет более двух раз, я предполагаю, что что-то пошло не так и вышло из программы, чтобы избежать бесконечного цикла, запрашивающего новый ввод. Это может быть или не быть хорошим способом сделать это, но это не влияет на вопрос.)

Проблема в том, что readLine() не запрашивает ввод пользователя во второй раз, после того как он получил end-of-line input (который дает значение nil). (End-линии могут быть введены с^D).

Это означает, что функция, где readLine() находится автоматически возвращает nil, потому что это последнее значение переменной, которая получит readLine().

Вопросы

  1. не должны readLine() назвать независимо от того, какое значение получающее переменная уже имеет?

  2. Если да, то почему пользователь не запрашивает ввод, когда nil был введен один раз?

Это код:

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). 
} 

Примечания

  • При вводе любой другой недействительный путь (любая строка, даже пустой (просто нажмите Enter), петля работает по назначению.
  • Первоначально path в функции askUserForWordList() было объявлено как конста nt (let path = readLine(stripNewline: true)), но я изменил его на var, так как он должен обновляться каждый раз, когда вызывается функция. Однако это не повлияло на работу программы.
  • Я пробовал объявить path отдельно на линии перед вызовом readLine(), что не имело значения.
  • Я попробовал пропустить переменную path полностью и позволить функции askUserForWordList() вернуть результат readLine() (return readLine(stripNewline: true)). Это не имело никакого значения.
  • Я пропустил функцию askUserForWordList() и переместил код с запросом ввода пользователем в «основной» код функции «getFilePathFromUser()», но это ничего не изменило.

    Модифицированный код:

    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

Что касается 'выхода (-46)', Swift коды выхода являются такими же, как и в C, так что только "стандартные" значения: 0 ('EXIT_SUCCESS') и 1 (' EXIT_FAILURE'). Если вы намерены выйти в любом случае, я бы предложил вместо этого использовать 'fatalError()', поскольку он выводит файл и номер строки начального сбоя, который помогает при отладке. – xoudini

ответ

1

readLine() возвращает nil, если (и только если) стандартный дескриптор входного файла достиг конца-оф-файла. Это происходит (например) при чтении ввода из tty, а Ctrl-D ("end-of-transmission character") вводится как первый символ в строке.

Все последующие readLine() звонки затем возвращают nil, а также нет Способ обнаружить, что «Ctrl-D был введен дважды». Другими словами, как только стандартный ввод находится в состоянии конца файла , вы больше не сможете читать данные. Если для ваших программ требуется дополнительных данных, тогда вы можете сообщить об ошибке, например.

guard let path = readLine(stripNewline: true) else { 
    throw InputError.UnexpectedEndOfFile 
} 
Смежные вопросы