2017-08-24 27 views
5

Tôi đang tìm kiếm một cách, trong Swift 4, để kiểm tra xem một Nhân vật có phải là một thành viên của một CharacterSet tùy ý hay không. Tôi có lớp này là Scanner sẽ được sử dụng cho một số phân tích cú pháp nhẹ. Một trong các hàm trong lớp là bỏ qua bất kỳ ký tự nào, ở vị trí hiện tại, thuộc về một tập hợp các ký tự nhất định.Cách tốt nhất để kiểm tra xem một ký tự có chứa một ký tự trong Swift 4 là gì?

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)\"") 

Tôi muốn triển khai chức năng skip(charactersIn:) để mã trên sẽ in buzz fizz.

Phần khó khăn là characters.contains(str[idx])) trong số while - .contains() yêu cầu số Unicode.Scalar và tôi không thể tìm ra bước tiếp theo.

Tôi biết tôi có thể vượt qua trong một String đến skip chức năng, nhưng tôi muốn tìm một cách để làm cho nó làm việc với một CharacterSet, vì tất cả các thành viên tĩnh thuận tiện (alphanumerics, whitespaces, vv).

Làm cách nào để kiểm tra CharacterSet nếu nó có chứa Character?

+0

Có một lớp hệ thống được gọi là 'NSScanner', được gắn vào Swift dưới dạng' Máy quét'. Bạn đã kiểm tra nó chưa? –

+0

NSScanner chắc chắn trông giống như một bánh xe tôi đang tái phát minh lúc đầu đỏ mặt. Không điên về ngữ nghĩa của NS (sử dụng một tham số 'NSString?'), Nhưng nó có thể thực hiện thủ thuật. Vì tò mò, tôi đã xem qua [source] (https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/Scanner.swift), và nó chuyển đổi một 'String' thành một mảng' ', và đó là' bỏ qua 'chức năng sau đó chỉ sử dụng 'set.contains (UnicodeScalar (currentCharacter)!)'. – PocketLogic

+0

Nếu bạn không thích ngữ nghĩa NS của 'NSScanner', hãy sử dụng' Máy quét' của Foundation, không sử dụng các loại NS. Chắc chắn không xác định lớp của riêng bạn với tên của một lớp hiện có. Điều đó sẽ trở nên khó hiểu. – Rob

Trả lời

3

Tôi biết rằng bạn muốn sử dụng CharacterSet thay vì String, nhưng CharacterSet không (chưa, ít nhất) các ký tự hỗ trợ bao gồm nhiều hơn một Unicode.Scalar. Xem ký tự "gia đình" (‍‍‍) hoặc các ký tự cờ quốc tế (ví dụ: "" hoặc "") mà Apple đã trình bày trong cuộc thảo luận chuỗi trong video WWDC 2017 What's New in Swift. Biểu tượng cảm xúc nhiều tông màu da cũng thể hiện hành vi này (ví dụ: so với).

Do đó, tôi sẽ cảnh giác với việc sử dụng CharacterSet (đây là "bộ giá trị ký tự Unicode để sử dụng trong hoạt động tìm kiếm"). Hoặc, nếu bạn muốn cung cấp phương pháp này để thuận tiện, hãy lưu ý rằng nó sẽ không hoạt động chính xác với các ký tự đại diện bởi nhiều vô hướng unicode.

Vì vậy, bạn có thể cung cấp một máy quét cung cấp cả CharacterSetString lồng vào cốt của phương pháp 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) 
     } 
    } 

} 

Vì vậy, ví dụ đơn giản của bạn vẫn sẽ làm việc:

let scanner = MyScanner("fizz buzz fizz") 
scanner.skip(charactersIn: CharacterSet.alphanumerics) 
scanner.skip(charactersIn: CharacterSet.whitespaces) 
print(scanner.remains) // "buzz fizz" 

Nhưng sử dụng String hiển thị nếu các ký tự bạn muốn bỏ qua có thể bao gồm nhiều vô hướng 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 

Như Michael Thác lưu ý trong các ý kiến ​​dưới đây, CharacterSet có một lỗi và thậm chí không xử lý 32-bit Unicode.Scalar giá trị một cách chính xác, có nghĩa là nó thậm chí không xử lý nhân vật vô hướng đơn đúng nếu giá trị vượt quá 0xffff (bao gồm biểu tượng cảm xúc, trong số những người khác). Tuy nhiên, các biểu hiện String, ở trên, xử lý các chính xác.

+2

Thú vị' CharacterSet' thậm chí không xử lý biểu tượng cảm xúc được biểu diễn bằng một unicode vô hướng (= 128518) tuy nhiên điều này trả về 'false': ' CharacterSet (charactersIn: "ABC"). chứa (UnicodeScalar (128518)!) ' –

+0

Vâng, trên và vượt quá giới hạn vô hướng đơn của bộ ký tự, rõ ràng là một lỗi trong 'CharacterSet' xử lý các scalar 32 bit, xử lý chúng như các vô hướng 16 bit. Ví dụ. thử tìm chuỗi của bạn cho 'Unicode.Scalar (62982)' (tức là '128518 && 0xffff'); lol. Tất cả đều hoạt động tốt và dandy với vô hướng 16 bit, nhưng đó là một xác tàu khi bạn thử sử dụng vô hướng 32 bit với các giá trị vượt quá 'UInt16.max'. Chúng ta nên gửi báo cáo lỗi. Tôi rất vui khi làm như vậy, trừ khi bạn muốn làm điều đó. – Rob

1

Không chắc chắn nếu đó là cách hiệu quả nhất nhưng bạn có thể tạo ra một charset mới và kiểm tra xem họ là phụ/siêu bộ (Set so sánh là khá nhanh)

let newSet = CharacterSet(charactersIn: "a") 
// let newSet = CharacterSet(charactersIn: "\(character)") 
print(newSet.isSubset(of: CharacterSet.decimalDigits)) // false 
print(newSet.isSubset(of: CharacterSet.alphanumerics)) // true 
Các vấn đề liên quan