2016-09-24 22 views
49

Tôi đã cập nhật một số mã và câu trả lời cũ của mình với Swift 3 nhưng khi tôi đã đến Swift Strings và lập chỉ mục nó đã là một nỗi đau để hiểu mọi thứ.String.Index hoạt động như thế nào trong Swift

Cụ thể là tôi đã cố gắng như sau:

let str = "Hello, playground" 
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) // error 

nơi dòng thứ hai đã đem lại cho tôi những lỗi sau

'advancedBy' không có sẵn: Để thúc đẩy một chỉ số của bước n gọi là 'chỉ số (_: offsetBy :) 'trên cá thể CharacterView tạo ra chỉ mục.

Tôi thấy rằng String có các phương pháp sau.

str.index(after: String.Index) 
str.index(before: String.Index) 
str.index(String.Index, offsetBy: String.IndexDistance) 
str.index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index) 

Điều này thực sự gây nhầm lẫn cho tôi lúc đầu nên tôi bắt đầu chơi với họ cho đến khi tôi hiểu chúng. Tôi đang thêm một câu trả lời dưới đây để hiển thị cách chúng được sử dụng.

Trả lời

144

enter image description here

Tất cả các ví dụ sau đây sử dụng

var str = "Hello, playground" 

startIndexendIndex

  • startIndex là chỉ số của ký tự đầu tiên
  • endIndex được chỉ số sau ký tự cuối cùng.

Ví dụ

// character 
str[str.startIndex] // H 
str[str.endIndex] // error: after last character 

// range 
let range = str.startIndex..<str.endIndex 
str[range] // "Hello, playground" 

Với Swift 4 của one-sided ranges, phạm vi có thể được đơn giản hóa với một trong các hình thức sau đây.

let range = str.startIndex... 
let range = ..<str.endIndex 

Tôi sẽ sử dụng biểu mẫu đầy đủ trong các ví dụ sau để rõ ràng, nhưng vì mục đích dễ đọc, có thể bạn sẽ muốn sử dụng các phạm vi một chiều trong mã của mình.

after

Như trong: index(after: String.Index)

  • after đề cập đến các chỉ số của nhân vật trực tiếp sau khi chỉ số nhất định.

Ví dụ

// character 
let index = str.index(after: str.startIndex) 
str[index] // "e" 

// range 
let range = str.index(after: str.startIndex)..<str.endIndex 
str[range] // "ello, playground" 

before

Như trong: index(before: String.Index)

  • before đề cập đến các chỉ số của nhân vật trực tiếp trước khi chỉ số nhất định.

Ví dụ

// character 
let index = str.index(before: str.endIndex) 
str[index] // d 

// range 
let range = str.startIndex..<str.index(before: str.endIndex) 
str[range] // Hello, playgroun 

offsetBy

Như trong: index(String.Index, offsetBy: String.IndexDistance)

  • Giá trị offsetBy thể là tích cực hay tiêu cực và bắt đầu từ chỉ số nhất định. Mặc dù thuộc loại String.IndexDistance, bạn có thể cung cấp cho nó Int.

Ví dụ

// character 
let index = str.index(str.startIndex, offsetBy: 7) 
str[index] // p 

// range 
let start = str.index(str.startIndex, offsetBy: 7) 
let end = str.index(str.endIndex, offsetBy: -6) 
let range = start..<end 
str[range] // play 

limitedBy

Như trong: index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)

  • Các limitedBy rất hữu ích cho việc bảo đảm rằng bù đắp không gây các chỉ số đi ra ngoài giới hạn. Nó là một chỉ số giới hạn. Vì có thể bù đắp vượt quá giới hạn, phương thức này trả về tùy chọn. Nó trả về nil nếu chỉ mục nằm ngoài giới hạn.

Ví dụ

// character 
if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) { 
    str[index] // p 
} 

Nếu bù đắp đã 77 thay vì 7, sau đó báo cáo kết quả if sẽ bị bỏ qua.

Tại sao String.Index cần?

Nó sẽ là nhiều dễ sử dụng chỉ mục Int cho chuỗi. Lý do bạn phải tạo một String.Index mới cho mỗi chuỗi là các ký tự trong Swift không phải là tất cả cùng độ dài dưới mui xe. Một ký tự Swift đơn có thể bao gồm một, hai hoặc thậm chí nhiều điểm mã Unicode hơn. Vì vậy, mỗi chuỗi duy nhất phải tính toán các chỉ mục của các ký tự của nó.

Có thể ẩn sự phức tạp này sau phần mở rộng chỉ mục Int, nhưng tôi không muốn làm như vậy. Nó là tốt để được nhắc nhở về những gì đang thực sự xảy ra.

+2

Tại sao 'startIndex' được bất cứ điều gì khác hơn 0? –

+4

@RoboRobok: Bởi vì Swift làm việc với các ký tự Unicode, được tạo thành từ "các cụm grapheme", Swift không sử dụng các số nguyên để biểu diễn các vị trí chỉ mục. Giả sử nhân vật đầu tiên của bạn là 'é'. Nó thực sự được tạo thành từ biểu thức Unicode '' '' '' '' '' '' ''. Nếu bạn đã sử dụng chỉ mục bằng 0, bạn sẽ nhận được ký tự 'e' hoặc dấu trọng âm (" mộ "), không phải toàn bộ cụm tạo thành' é'. Sử dụng 'startIndex' đảm bảo bạn sẽ nhận được toàn bộ cụm grapheme cho bất kỳ ký tự nào. – leanne

+1

Trong Swift 4.0, mỗi ký tự Unicode được tính bằng 1. Ví dụ: "‍" .count // Bây giờ: 1, Trước: 2 – selva

Các vấn đề liên quan