Swift中不区分大小写的开关语句?

4

我正在尝试使用一个接受用户输入并打印简单输出的应用程序。

我的代码如下:

switch text {
        case "Hi", "hi", "Hello", "hello", "Good day", "good day":
            return "Hello, sir."
        default:
            return "Sorry, I didn't understand!"
}

我的问题是,是否有可能只提供一个不区分大小写的输入,而不是为一个输出提供5-10个可能的输入?例如,我可以只输入“hello”,然后让程序检查“Hello”、“hello”、“HELLO”等。


4
在你的switch语句中,使用text.lowercased()并在不同的情况下输入仅包含小写字母的字符串。请注意,当对某些特殊字符应用lowercased()uppercased()时,它们可能不会按预期进行转换。有关详情,请查看以下问答(特别是问题的评论部分)。 - dfrib
3个回答

11
你可以将text转换为大写(或者像评论中提到的那样使用lowercased()将其转换为小写),这样大小写就不会影响结果:
switch text.uppercased() {
case "HI", "HELLO", "GOOD DAY":
  return "Hello, sir."
default:
  return "Sorry, I didn't understand!"
}

5
最优雅的解决方案是使用不太为人知的 String.CompareOptions.range(of:, options:) 结合使用。这样可以以更少的工作量执行更复杂的操作,例如 匹配时忽略特殊字符,而不仅限于大小写,并使用正则表达式(这对您的特定目的非常有利)。如果您不知道它们是什么,我建议您了解一下。
回到主题: 您可以在此处阅读有关 String.CompareOptions 的更多信息。文档适用于 NSString,但也适用于 String
然后,您可以按以下方式使用它:
let userInput = "HeLLö, how are you doing? Greetings!"

let acceptedWords = ["hello", "hi", "greeting"]

var resultCommants = acceptedWords.filter { (word) -> Bool in
    return userInput.range(of: word, options: [.caseInsensitive, .diacriticInsensitive]) != nil
}

// resultCommands = ["hello", "greeting"]

正如您所看到的,HeLLö 匹配 hello,而 Greetings 匹配 greeting 是因为使用了 .caseInsensitive.diacriticInsensitive

如果您不熟悉 .filter 运算符,它会遍历您的数组,并且对于每个项目 word,期望您返回 true 或 false,具体取决于该项目是否应包含在过滤后的数组版本中。这是通过将 .range(of:, options:) 的结果与 nil 进行比较来完成的。不相等的 nil 表示找到了该单词,相等的 nil 表示未找到。当然,如果您想知道单词的位置,也可以使用范围。


如果我们想要结合几个比较选项,那么这种方法是不错的,但是我认为在这种情况下使用compare(_:options:)和测试 vs .orderedSamerange(...) 测试 vs 非 nil具有更好的语义(因为在这种情况下,我们实际上并没有利用由调用range(of:options:)生成的范围)。 - dfrib
是的,你说得对。最终归结为相同的问题。对我来说重要的是利用options,特别是.diacriticInsensitive,否则实现起来将会非常困难(.caseInsensitive则简单得多)。 - Guillermo Suárez
我希望他们实现了contains(_:options:),以便在更简单、更清晰的布尔函数中使用比较选项。 - Guillermo Suárez
你可以将 contains(_:contains:) -> ComparisonResult 重载为返回 Bool 的方法,其中你自己的实现只需调用先前的方法并将结果与 .orderedSame 进行比较(以产生 Bool 返回值)。你选择的重载将从上下文中推断出来(例如,当作为 filter 的谓词时使用你自定义的方法)。 - dfrib
当然可以 :) 但是我仍然觉得奇怪的是,这种方法还没有正式地进行重载。(这将意味着更多的人使用高级选项,并希望不实现错误的匹配逻辑)。 - Guillermo Suárez
我同意官方可能会有这样的方法,但是我认为API命名方法(约定)的指南将使compare作为这种重载的有效选择无效。 用于测试某些给定比较选项的“相等性”(或者更确切地说,相同的排序)的方法可能会有一个类似isOrderedSame(as:compareOptions:)的名称,或者类似这样的名称 :) - dfrib

2
一种替代方案:
func check(input: String) -> Bool {
    let strings = ["Hi", "hi", "Hello", "hello", "Good day", "good day"]

    let inputLowercase = input.lowercased()
    return strings.first(where: { $0.lowercased() == inputLowercase }) != nil
}

3
请注意,每当您使用类似于“...first (where: ...)!= nil”的语句时,例如查找给定谓词的某些元素,但没有利用该元素时,您可以简单地使用“contains( where: ...)”代替。例如,在上面的示例中,可以使用“return strings.contains(where:{$0.lowercased()== input.lowercased()})”。然而,由于您不是应用模式匹配,因此没有理由不使用已经可用的现有不区分大小写的比较工具(在这里来自“Foundation”):例如“return strings.contains(where:{$0.caseInsensitiveCompare(input)==。orderedSame})”。 - dfrib

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接