Подтвердить что ты не робот

NSCharacterSet.characterIsMember() с типом Swift

Представьте, что у вас есть экземпляр типа Swift Character, и вы хотите определить, является ли он членом NSCharacterSet. NSCharacterSet characterIsMember принимает значение unichar, поэтому нам нужно получить от Character до unichar.

Единственное решение, которое я мог бы придумать, следующее: где c - мой Character:

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

Я посмотрел на Character, но ничего не выскользнуло на меня как способ получить от него до unichar. Это может быть связано с тем, что Character более общий, чем unichar, поэтому прямое преобразование не будет безопасным, но я только предполагаю.

Если бы я повторял целую строку, я бы сделал что-то вроде этого:

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

(Предупреждение: вышеперечисленное является псевдокодом и никогда не управляется кем-либо когда-либо). Но на самом деле это не то, о чем я прошу.

4b9b3361

Ответ 1

Я понимаю, что unichar является typealias для UInt16. A unichar - это просто число.

Я думаю, что проблема, с которой вы сталкиваетесь, заключается в том, что Character в Swift может состоять из нескольких символов "Юникод". Таким образом, он не может быть преобразован в одно значение unichar, поскольку он может состоять из двух unichars. Вы можете разложить Character на свои индивидуальные значения unichar, переведя его в строку и используя свойство utf16, например:

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

Это приведет к созданию массива - codeUnits - значений unichar.

EDIT: начальный код имел 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

Теперь сделайте проверку функции для всех значений 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" ), так и комбинирующего символа (острый акцент) в наборе символов или вы получите ложные срабатывания.

Ответ 2

Я бы рассматривал символ как строку и 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

Ответ 3

Сделайте все в одном лайнере:

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

(Swift 3)

Ответ 4

Из-за изменений в Swift 3.0 матовый ответ больше не работает, поэтому здесь рабочая версия (как расширение):

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 native 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
    }
}

или do:

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

Примечание: вышеописанный сбой и не работает из-за https://bugs.swift.org/browse/SR-3667. Не уверен, что CharacterSet получает такую ​​любовь, в которой он нуждается.