Swift 4)で、Characterが任意のCharacterSetのメンバーであるかどうかをテストする方法を探しています。このScanner
クラスを使用して、軽量解析:クラス内の関数の1つは、現在の位置で、特定の可能な文字セットに属する文字をスキップすることです。
_class MyScanner {
let str: String
var idx: String.Index
init(_ string: String) {
str = string
idx = str.startIndex
}
var remains: String { return String(str[idx..<str.endIndex])}
func skip(charactersIn characters: CharacterSet) {
while idx < str.endIndex && characters.contains(str[idx])) {
idx = source.index(idx, offsetBy: 1)
}
}
}
let scanner = MyScanner("fizz buzz fizz")
scanner.skip(charactersIn: CharacterSet.alphanumerics)
scanner.skip(charactersIn: CharacterSet.whitespaces)
print("what remains: \"\(scanner.remains)\"")
_
上記のコードが_buzz fizz
_を出力するように、skip(charactersIn:)
関数を実装したいと思います。
トリッキーな部分はwhile
のcharacters.contains(str[idx]))
です-.contains()
には_Unicode.Scalar
_が必要で、次のステップを見つけようとして途方に暮れています。
String
をskip
関数に渡すことはできますが、すべての便利さのために、CharacterSet
で機能させる方法を見つけたいと思います。静的メンバー(alphanumerics
、whitespaces
など)。
CharacterSet
が含まれている場合、どのようにしてCharacter
をテストしますか?
CharacterSet
ではなくString
を使用したいことを知っていますが、CharacterSet
は複数のUnicode.Scalar
で構成される文字を(少なくとも)サポートしていません。 「家族」キャラクター(???? ???? ???? ????)または国際旗キャラクター(例えば、「????????」または「????」 ???? ")that Apple WWDC 2017ビデオの文字列ディスカッションで示されています Swiftの新機能 。複数のスキントーン絵文字もこの動作を示しています(? ???????対????????)。
そのため、CharacterSet
(「検索操作で使用するUnicode文字値のセット」)の使用には注意が必要です。または、便宜上このメソッドを提供する場合は、複数のUnicodeスカラーで表される文字では正しく機能しないことに注意してください。
したがって、CharacterSet
メソッドのString
とskip
の両方のレンディションを提供するスキャナーを提供できます。
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"
ただし、スキップする文字に複数のUnicodeスカラーが含まれる場合は、String
レンディションを使用します。
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
Michael Waterfallが以下のコメントで指摘したように、CharacterSet
にはバグがあり、32ビットのUnicode.Scalar
値を正しく処理することさえできません。つまり、値が0xffff
(絵文字など)。ただし、上記のString
レンディションはこれらを正しく処理します。
それが最も効率的な方法かどうかはわかりませんが、新しいCharSetを作成して、それらがサブ/スーパーセットであるかどうかを確認できます(セットの比較はかなり速いです)
let newSet = CharacterSet(charactersIn: "a")
// let newSet = CharacterSet(charactersIn: "\(character)")
print(newSet.isSubset(of: CharacterSet.decimalDigits)) // false
print(newSet.isSubset(of: CharacterSet.alphanumerics)) // true
Swift 4.2CharacterSet
が含まれているかどうかを確認するCharacter
拡張関数:
extension CharacterSet {
func containsUnicodeScalars(of character: Character) -> Bool {
return character.unicodeScalars.allSatisfy(contains(_:))
}
}
使用例:
CharacterSet.decimalDigits.containsUnicodeScalars(of: "3") // true
CharacterSet.decimalDigits.containsUnicodeScalars(of: "a") // false