Странное поведение в методе CharacterSet.contains () с символами высокого UTF8, смешанными с ASCII - PullRequest
2 голосов
/ 09 июля 2020

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

Проблема заключается в том, что у меня возникают трудности при сравнении с набором символов, который содержит ОБЕ символы ASCII с малым числом И символы UTF8 с большим числом.

Если я представляю только UTF8 high или ASCII, совпадение работает нормально.

Я создал игровую площадку, которая иллюстрирует это.

Странный результат - предпоследняя распечатка («Test String 2 does not have a space or a joker.»). Здесь должно быть написано «делает».

Проблема в том, что пространство в CharacterSet совпадает, а карта-джокер - нет.

Есть идеи? Вот площадка:

import Foundation

public extension StringProtocol {
    func containsOneOfThese(_ inCharacterset: CharacterSet) -> Bool {
        self.contains { (char) in
            char.unicodeScalars.contains { (scalar) in inCharacterset.contains(scalar) }
        }
    }
}

let space = " "
let joker = "?"
let both = space + joker

let spadesNumberCards = "??????????"
let spadesFaceCards = "????"

let testString1 = spadesNumberCards + space + spadesFaceCards
let testString2 = spadesNumberCards + joker + spadesFaceCards
let testString3 = spadesNumberCards + both + spadesFaceCards

print("These Are The Strings We Are Testing:\n")
print("Test String 1: \"\(testString1)\"")
print("Test String 2: \"\(testString2)\"")
print("Test String 3: \"\(testString3)\"")
      
print("\nFirst, See If Any Of the Strings Contain Spaces:\n")
print("Test String 1 does \(testString1.containsOneOfThese(CharacterSet(charactersIn: space)) ? "" : "not ")have a space.")
print("Test String 2 does \(testString2.containsOneOfThese(CharacterSet(charactersIn: space)) ? "" : "not ")have a space.")
print("Test String 3 does \(testString3.containsOneOfThese(CharacterSet(charactersIn: space)) ? "" : "not ")have a space.")

print("\nNext, See If Any Of the Strings Contain Jokers:\n")
print("Test String 1 does \(testString1.containsOneOfThese(CharacterSet(charactersIn: joker)) ? "" : "not ")have a joker.")
print("Test String 2 does \(testString2.containsOneOfThese(CharacterSet(charactersIn: joker)) ? "" : "not ")have a joker.")
print("Test String 3 does \(testString3.containsOneOfThese(CharacterSet(charactersIn: joker)) ? "" : "not ")have a joker.")

print("\nOK, Now it gets weird:\n")

print("Test String 1 does \(testString1.containsOneOfThese(CharacterSet(charactersIn: both)) ? "" : "not ")have a space or a joker.")
print("Test String 2 does \(testString2.containsOneOfThese(CharacterSet(charactersIn: both)) ? "" : "not ")have a space or a joker.")
print("Test String 3 does \(testString3.containsOneOfThese(CharacterSet(charactersIn: both)) ? "" : "not ")have a space or a joker.")

Что распечатывает:

These Are The Strings We Are Testing:

Test String 1: "?????????? ????"
Test String 2: "???????????????"
Test String 3: "?????????? ?????"

First, See If Any Of the Strings Contain Spaces:

Test String 1 does have a space.
Test String 2 does not have a space.
Test String 3 does have a space.

Next, See If Any Of the Strings Contain Jokers:

Test String 1 does not have a joker.
Test String 2 does have a joker.
Test String 3 does have a joker.

OK, Now it gets weird:

Test String 1 does have a space or a joker.
Test String 2 does not have a space or a joker.
Test String 3 does have a space or a joker.

1 Ответ

1 голос
/ 09 июля 2020

Кажется, что CharacterSet.init(charactersIn string: String) не работает правильно, если строка содержит символы как изнутри, так и вне BMP (базовая c многоязычная плоскость):

let s = " ?"
let cs = CharacterSet(charactersIn: s)
s.unicodeScalars.forEach {
    print(cs.contains($0))
}

// Expected output: true, true
// Actual output:   true, false

Обходной путь - использовать создание вместо этого набор символов из последовательности скаляров Unicode:

let cs = CharacterSet(s.unicodeScalars)

Это даст ожидаемый результат.

Но обратите внимание, что это не может обрабатывать весь диапазон Swift Character s (который включает кластеры графем, состоящие из нескольких скаляров Unicode). Поэтому вы можете вместо этого работать с Set<Character>.

...