【问题标题】:Calling Swift's readline to the same variable twice ignores the second call两次调用 Swift 的 readline 到同一个变量会忽略第二次调用
【发布时间】:2016-07-17 20:25:18
【问题描述】:

我正在从命令行读取用户输入,检查它是否是有效的文件路径,如果不是,请用户再试一次。

如果用户输入的是nil,第一次应该被当作任何其他错误输入,让用户输入一个新的值,但是第二次输入nil,程序应该强制退出.

(我假设nil 值是用户不会故意输入的,所以如果它发生两次以上,我假设出现问题并退出程序以避免无休止的循环要求新的输入。这可能是也可能不是一个好方法,但这不会影响问题。)

问题是readLine() 在收到行尾输入(产生nil 值)后第二次调用时不会要求用户输入。 (行尾可以用^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))时,循环将按预期工作。
  • 最初askUserForWordList() 函数中的path 被声明为常量(let path = readLine(stripNewline: true)),但我将其更改为var,因为它应该在每次调用函数时更新。不过,这并不影响程序的运行方式。
  • 我尝试在调用readLine()之前在行上单独声明path,没有任何区别。
  • 我尝试完全跳过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!
    }
    

【问题讨论】:

  • 关于 exit(-46),Swift 退出代码与 C 中的相同,因此唯一的“标准”值是 0 (EXIT_SUCCESS) 和 1 (EXIT_FAILURE)。如果您仍然打算退出,我建议改用fatalError(),因为它会输出原始崩溃的文件和行号,这有助于调试。

标签: swift null variable-assignment readline


【解决方案1】:

readLine() 返回nil 如果(且仅当)标准输入文件描述符已到达文件末尾。这发生在(例如)读取输入时 来自 tty,并输入 Ctrl-D"end-of-transmission character")作为一行的第一个字符。

所有后续的readLine() 调用然后也返回nil,没有 方式检测到“Ctrl-D 输入了两次”。 换句话说,一旦标准输入处于文件结束状态 您无法再从中读取任何数据。如果您的程序需要 数据多了就只能报错,例如

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    • 1970-01-01
    相关资源
    最近更新 更多