12/08/2018, 16:35

Substring trong Swift

Khi nào là một Sub-String mà k phải là một String? Text string trong ngôn ngữ lập trình bao gồm các ký tự và các quy tắc để cấu thành chúng. Với một ngôn ngữ được ưa thích là C, String là mảng các ký tự nhưng thay vì phải nhập theo dạng mảng ký tự ['h','e','l','l','o'] bạn chỉ cần gõ ...

Khi nào là một Sub-String mà k phải là một String?

Text string trong ngôn ngữ lập trình bao gồm các ký tự và các quy tắc để cấu thành chúng. Với một ngôn ngữ được ưa thích là C, String là mảng các ký tự nhưng thay vì phải nhập theo dạng mảng ký tự ['h','e','l','l','o'] bạn chỉ cần gõ "hello" và trinh biên dịch sẽ hỗ trợ hỗ trợ nó. Với các ngôn ngữ bậc cao như Swift, xử lý String với nhiều mảng ký tự và đầy đủ các tính năng của nó. Điều đầu tiên nhìn thấy ở String, chung ta thấy một phần của chúng: các chuỗi con( Sub-string)

Trước tiên, hay nhìn cách String được thực hiện trong String.swift trong thư viện chuẩn

public struct String {
  public var _core: _StringCore
}

Có một số khởi tạo, nhưng chỉ có một property trong khai báo chính, các cả thành phần khác ở trong [StringCore.swift:]

public struct _StringCore {
  public var _baseAddress: UnsafeMutableRawPointer?
  var _countAndFlags: UInt
  public var _owner: AnyObject?
}

Có rất nhiều kiểu, hãy tập trung vào stored properties:

  • Base address - con trỏ tới vùng lưu trữ
  • Count - chiều dài của chuỗi được lưu dạng bit Count — length of the string, stored in the lower (UInt.bitWidth - 2) bits of _countAndFlags. Trên 64 bit runtime, điều này có ngĩa 62 bit cung cấp độ dài chuối tối đa
  • Flags - Bit đầu tiên để xác định String được hỗ trợ bởi Swift native _StringBuffer hoặc NSString-style Cocoa buffer. Bit thư hai để xác định là ASCII hay UTF-16.

_StringCore nhiều phần phực tạp. Nhưng nếu nhìn một cách đơn giản nhất là Strong bồm phần lưu trữ và kích thước.

Làm thế nào để tạo ra một Sub-string trong Swift. Đơn giản là tách một của string như subscript:

let str = "Hello Swift!"

let slice = str[str.startIndex..<str.index(str.startIndex, offsetBy: 5)]
// "Hello"

Rất đơn giản phải không. startIndex là bước nhảy, index(_:offsetBy:) là độ lớn mong muốn Với việc bắt đầu ở startIndex chúng ta có thể đơn giản hơn

let withPartialRange = str[..<str.index(str.startIndex, offsetBy: 5)]
// still "Hello"

Hoặc

let slice = str.prefix(5)
// still "Hello"

String là colloections nên bạn có thể sử dung prefix(), suffix(), dropFirst(), ...

Inside a Substring

Sự kỳ diệu của substring là sử dụng lại lưu trữ của string mẹ Điều đó có nghĩa là chuỗi 100 ký tự của chuỗi 8000 ký tự không cần được cấp phát và sao lưu lại. Điều đó có nghĩa là bạn có thể kéo dài timelife của một base string. Vậy chính xác bên trong substring giư điều gì?

public struct Substring {
  internal var _slice: RangeReplaceableBidirectionalSlice<String>

_slice property sẽ giữ toàn bộ thông tin cần thiết từ base string

// Still inside Substring
internal var _wholeString: String {
  return _slice._base
}
public var startIndex: Index { return _slice.startIndex }
public var endIndex: Index { return _slice.endIndex }

Việc tính toán Propertie _wholeString (trả về toàn bộ chuỗi ban đầu), cũng như startIndex và endIndex (xác định phần nào của chuỗi để cắt) chỉ cần đi qua các thuộc tính slice bên dưới. Bạn cũng có thể thấy phần slice giữ trên chuỗi ban đầu bằng _base.

Substring to String

Vậy khi bạn có Substring, nhưng chức năng bạn mong muốn một String. Không cần lo lăng, bạn có thể dễ dàng chuyển đổi Substring thành String

let string = String(substring)

Vì Substring chia sẻ vùng nhớ với base string của chúng, việc tạo ra chuỗi mới sẽ tạo vùng nhớ mới. Việc khởi tạo String mới chứa một substring sẽ diễn ra thế nào?

extension String {
  public init(_ substring: Substring) {
    // 1
    let x = substring._wholeString
    // 2
    let start = substring.startIndex
    let end = substring.endIndex
    // 3
    let u16 = x._core[start.encodedOffset..<end.encodedOffset]
    // 4A
    if start.samePosition(in: x.unicodeScalars) != nil
    && end.samePosition(in: x.unicodeScalars) != nil {
      self = String(_StringCore(u16))
    }
    // 4B
    else {
      self = String(decoding: u16, as: UTF16.self)
    }
  }
}
  1. Lấy tham chiếu đến toàn bộ base string.
  2. Lấy giá trị của start và end.
  3. lấy _StringCore dạng UTF-16 và encodedOffset
  4. Kiếm tra việc matching Unicode có phù hợp hay không. Brand A có nghĩa là bạn không một chia sẻ nào và có thể tạo mới với _StringCore trên UTF-16. Hoăc Brand B sẽ là giải mã UTF-16 buffer để tạo ra một chuỗi mới.

StringProtocol là một điều tuyệt vời của lập trình hướng đối tượng. Nó tóm gọn các chức năng của String như uppercased(), lowercased(), being comparable, hashable, a collection, etc. Cả String và substring đều tuân thủ StringProtocol Điều đó có nghĩa bạn thể sử dụng phép so sánh == mà không phải thực hiện bước converte gì

let helloSwift = "Hello Swift"
let swift = helloSwift[helloSwift.index(helloSwift.startIndex, offsetBy: 6)...]

// comparing a substring to a string             
0