我知道你想使用CharacterSet
而不是String
,但CharacterSet
不支持(但至少)支持由多个Unicode.Scalar
组成的字符。请参阅Apple在WWDC 2017视频What's New in Swift的字符串讨论中演示的“家庭”字符()或国际标记字符(例如“”或“”)。多肤色表情符号也表现出这种行为(例如vs)。
因此,我会谨慎使用CharacterSet
(这是一组“用于搜索操作的Unicode字符值”)。或者,如果您想为方便起见而提供此方法,请注意,它将无法正确使用由多个unicode标量表示的字符。
所以,你可能会提供一个扫描仪,提供了skip
方法既CharacterSet
和String
引渡:
class MyScanner {
let string: String
var index: String.Index
init(_ string: String) {
self.string = string
index = string.startIndex
}
var remains: String { return String(string[index...]) }
/// Skip characters in a string
///
/// This rendition is safe to use with strings that have characters
/// represented by more than one unicode scalar.
///
/// - Parameter skipString: A string with all of the characters to skip.
func skip(charactersIn skipString: String) {
while index < string.endIndex, skipString.contains(string[index]) {
index = string.index(index, offsetBy: 1)
}
}
/// Skip characters in character set
///
/// Note, character sets cannot (yet) include characters that are represented by
/// more than one unicode scalar (e.g. or or). If you want to test
/// for these multi-unicode characters, you have to use the `String` rendition of
/// this method.
///
/// This will simply stop scanning if it encounters a multi-unicode character in
/// the string being scanned (because it knows the `CharacterSet` can only represent
/// single-unicode characters) and you want to avoid false positives (e.g., mistaking
/// the Jamaican flag, , for the Japanese flag,).
///
/// - Parameter characterSet: The character set to check for membership.
func skip(charactersIn characterSet: CharacterSet) {
while index < string.endIndex,
string[index].unicodeScalars.count == 1,
let character = string[index].unicodeScalars.first,
characterSet.contains(character) {
index = string.index(index, offsetBy: 1)
}
}
}
因此,您简单的例子仍然可以工作:
let scanner = MyScanner("fizz buzz fizz")
scanner.skip(charactersIn: CharacterSet.alphanumerics)
scanner.skip(charactersIn: CharacterSet.whitespaces)
print(scanner.remains) // "buzz fizz"
但使用String
如果要跳过的字符可能包含多个Unicode标量:
let family = "\u{200D}\u{200D}\u{200D}" //
let boy = ""
let charactersToSkip = family + boy
let string = boy + family + "foobar" // foobar
let scanner = MyScanner(string)
scanner.skip(charactersIn: charactersToSkip)
print(scanner.remains) // foobar
正如迈克尔瀑布下面的评论中指出,CharacterSet
有缺陷,甚至不正确地处理32位Unicode.Scalar
值,这意味着它甚至不正确,如果该值超过0xffff
处理单个标字符(包括表情符号等)。然而,上面的String
演绎处理正确。
来源
2017-08-25 03:56:35
Rob
有一个名为'NSScanner'的系统类,以'Scanner'的形式桥接到Swift中。你检查过它吗? –
NSScanner肯定看起来像我正在重新发明的轮子。不是疯狂的NS语义(使用in'NSString?'参数),但它可能会伎俩。出于好奇,我浏览了[source](https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/Scanner.swift),并将'String'转换为'Array',它是'skip'函数,然后使用'set.contains(UnicodeScalar(currentCharacter)!)'。 –
PocketLogic
如果您不喜欢'NSScanner'的NS语义,请使用Foundation的'Scanner',它不使用NS类型。当然,不要用现有类的名称来定义自己的类。这只会让人困惑。 – Rob