【问题标题】:NSCharacterSet.characterIsMember() with Swift's Character typeNSCharacterSet.characterIsMember() 与 Swift 的 Character 类型
【发布时间】:2014-12-29 23:27:24
【问题描述】:

假设您有一个 Swift 的 Character 类型的实例,并且您想确定它是否是 NSCharacterSet 的成员。 NSCharacterSetcharacterIsMember方法需要一个unichar,所以我们需要从Characterunichar

我能想到的唯一解决方案如下,其中c 是我的Character

let u: unichar = ("\(c)" as NSString).characterAtIndex(0)
if characterSet.characterIsMember(u) {
    dude.abide()
}

我查看了Character,但没有任何东西让我从它那里跳到unichar。这可能是因为Characterunichar 更通用,所以直接转换并不安全,但我只是猜测。

如果我要迭代整个字符串,我会这样做:

let s = myString as NSString
for i in 0..<countElements(myString) {
    let u = s.characterAtIndex(i)
    if characterSet.characterIsMember(u) {
        dude.abide()
    }
}

(警告:以上是伪代码,从未被任何人运行过。)但这并不是我真正要问的。

【问题讨论】:

    标签: swift


    【解决方案1】:

    我的理解是unicharUInt16 的类型别名。 unichar 只是一个数字。

    我认为您面临的问题是 Swift 中的 Character 可以由多个 unicode“字符”组成。因此,它不能转换为单个 unichar 值,因为它可能由两个 unichars 组成。您可以通过将 Character 转换为字符串并使用 utf16 属性将其分解为单独的 unichar 值,如下所示:

    let c: Character = "a"
    let s = String(c)
    var codeUnits = [unichar]()
    for codeUnit in s.utf16 {
        codeUnits.append(codeUnit)
    }
    

    这将产生一个数组 - codeUnits - unichar 值。

    编辑:初始代码有for codeUnit in s,而它应该是for codeUnit in s.utf16

    您可以整理并测试每个 unichar 值是否在这样的字符集中:

    let char: Character = "\u{63}\u{20dd}" // This is a 'c' inside of an enclosing circle
    for codeUnit in String(char).utf16 {
        if NSCharacterSet(charactersInString: "c").characterIsMember(codeUnit) {
            dude.abide()
        } // dude will abide() for codeUnits[0] = "c", but not for codeUnits[1] = 0x20dd (the enclosing circle)
    }
    

    或者,如果您只对第一个(通常也是唯一的)unichar 值感兴趣:

    if NSCharacterSet(charactersInString: "c").characterIsMember(String(char).utf16[0]) {
        dude.abide()
    }
    

    或者,将其包装在一个函数中:

    func isChar(char: Character, inSet set: NSCharacterSet) -> Bool {
        return set.characterIsMember(String(char).utf16[0])
    }
    
    let xSet = NSCharacterSet(charactersInString: "x")
    isChar("x", inSet: xSet)  // This returns true
    isChar("y", inSet: xSet)  // This returns false
    

    现在让函数检查组合字符中的 all unichar 值 - 这样,如果您有组合字符,则函数仅在基本字符和组合字符都返回 true 时字符存在:

    func isChar(char: Character, inSet set: NSCharacterSet) -> Bool {
        var found = true
        for ch in String(char).utf16 {
            if !set.characterIsMember(ch) { found = false }
        }
        return found
    }
    
    let acuteA: Character = "\u{e1}"                   // An "a" with an accent
    let acuteAComposed: Character = "\u{61}\u{301}"    // Also an "a" with an accent
    
    // A character set that includes both the composed and uncomposed unichar values
    let charSet = NSCharacterSet(charactersInString: "\u{61}\u{301}\u{e1}")
    
    isChar(acuteA, inSet: charSet)           // returns true
    isChar(acuteAComposed, inSet: charSet)   // returns true (both unichar values were matched
    

    最后一个版本很重要。如果您的Character 是组合字符,则必须检查字符集中是否存在基本字符(“a”)和组合字符(重音符号),否则会出现误报。

    【讨论】:

    • 这很有趣。它不能完全解决我的问题,所以我不能将其标记为答案,但它确实解释了为什么没有直接的Character -> unichar
    • 我稍微完善了我的答案。请注意,matt 的答案虽然是检查非组合字符的好方法,但可能不适用于组合字符。
    • 我还在消化这个。令人着迷。
    • 几天前我在这个问题上陷入了困境,但我还没有回来:)
    • 短:let units = [unichar](String(char).utf16)
    【解决方案2】:

    我会将 Character 视为字符串,让 Cocoa 完成所有工作:

    func charset(cset:NSCharacterSet, containsCharacter c:Character) -> Bool {
        let s = String(c)
        let ix = s.startIndex
        let ix2 = s.endIndex
        let result = s.rangeOfCharacterFromSet(cset, options: nil, range: ix..<ix2)
        return result != nil
    }
    

    下面是如何使用它:

    let cset = NSCharacterSet.lowercaseLetterCharacterSet()
    let c : Character = "c"
    let ok = charset(cset, containsCharacter:c) // true
    

    【讨论】:

    • 正如我所怀疑的那样。基本上,您必须将 Character 变成 String 才能做到这一点。
    • 我无耻地盗取了你的代码并把它变成了NSCharacterSet上的扩展方法。
    • 这可以简化为let result = s.rangeOfCharacterFromSet(cset),因为“options:”和“range:”是(现在?)可选参数。
    • @MartinR 同意。我认为发生的事情是,当我处理该代码时,较短的版本没有出现在 Xcode 的代码完成中(现在仍然没有)。 - 它也不在标头的 Swift 版本中。我想知道这是否是 Objective-C 标头转换为 Swift 的方式中的一个错误。
    • @MartinR 同样的问题,例如stringByReplacingOccurrencesOfString:withString:rangeOfString:options:。这些较短的版本不会出现在 Swift 标头或代码完成中。解决方法是将字符串转换为 NSString - 然后它们会出现。这意味着 Swift 不能调用较短的版本,但它可以。我不得不将其视为一个错误。
    【解决方案3】:

    一次性完成所有操作:

    validCharacterSet.contains(String(char).unicodeScalars.first!)
    

    (斯威夫特 3)

    【讨论】:

      【解决方案4】:

      由于 Swift 3.0 的变化,matt 的答案不再有效,所以这里是工作版本(作为扩展):

      private extension NSCharacterSet {
      
        func containsCharacter(c: Character) -> Bool {
      
             let s = String(c)
             let ix = s.startIndex
             let ix2 = s.endIndex
             let result = s.rangeOfCharacter(from: self as CharacterSet, options: [], range: ix..<ix2)
             return result != nil
        }
      }
      

      【讨论】:

        【解决方案5】:

        Swift 3.0 的变化意味着您实际上不需要再桥接NSCharacterSet,您可以使用 Swift 的原生 CharacterSet

        你可以直接做类似于 Jiri 的回答:

        extension CharacterSet {
            func contains(_ character: Character) -> Bool {
                let string = String(character)
                return string.rangeOfCharacter(from: self, options: [], range: string.startIndex..<string.endIndex) != nil
            }
        }
        

        或者做:

        func contains(_ character: Character) -> Bool {
            let otherSet = CharacterSet(charactersIn: String(character))
            return self.isSuperset(of: otherSet)
        }
        

        注意:由于https://bugs.swift.org/browse/SR-3667,上述崩溃并且不起作用。不确定CharacterSet 得到了它需要的那种爱。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-02
          相关资源
          最近更新 更多