Jawaban:
Diperbarui untuk Swift 4
Rentang Swift lebih kompleks daripada NSRange
, dan tidak menjadi lebih mudah di Swift 3. Jika Anda ingin mencoba memahami alasan di balik beberapa kerumitan ini, baca ini dan ini . Saya hanya akan menunjukkan cara membuatnya dan kapan Anda mungkin menggunakannya.
a...b
Operator rentang ini membuat rentang Swift yang menyertakan elemen a
dan elemen b
, meskipun b
nilai maksimum yang mungkin untuk suatu tipe (suka Int.max
). Ada dua jenis rentang tertutup: ClosedRange
dan CountableClosedRange
.
ClosedRange
Elemen dari semua rentang di Swift sebanding (yaitu, mereka sesuai dengan protokol Comparable). Itu memungkinkan Anda mengakses elemen dalam rentang dari koleksi. Berikut ini contohnya:
let myRange: ClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
Namun, a ClosedRange
tidak dapat dihitung (yaitu, tidak sesuai dengan protokol Urutan). Itu berarti Anda tidak dapat mengulang elemen dengan for
loop. Untuk itu Anda membutuhkan file CountableClosedRange
.
CountableClosedRange
Ini mirip dengan yang terakhir kecuali sekarang rentangnya juga dapat diulang.
let myRange: CountableClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
for index in myRange {
print(myArray[index])
}
a..<b
Operator rentang ini menyertakan elemen a
tetapi bukan elemen b
. Seperti di atas, ada dua jenis rentang setengah terbuka: Range
dan CountableRange
.
Range
Seperti halnya ClosedRange
, Anda dapat mengakses elemen koleksi dengan file Range
. Contoh:
let myRange: Range = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
Namun, sekali lagi, Anda tidak dapat mengulangi a Range
karena hanya dapat dibandingkan, tidak dapat dilangkah.
CountableRange
A CountableRange
memungkinkan iterasi.
let myRange: CountableRange = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
for index in myRange {
print(myArray[index])
}
Anda NSRange
kadang-kadang masih dapat (harus) menggunakan di Swift (saat membuat string yang diatribusikan , misalnya), jadi akan sangat membantu untuk mengetahui cara membuatnya.
let myNSRange = NSRange(location: 3, length: 2)
Perhatikan bahwa ini adalah lokasi dan panjang , bukan indeks awal dan indeks akhir. Contoh di sini serupa artinya dengan rentang Swift 3..<5
. Namun, karena jenisnya berbeda, mereka tidak dapat dipertukarkan.
Operator ...
dan ..<
jangkauan adalah cara cepat untuk membuat rentang. Sebagai contoh:
let myRange = 1..<3
Cara tangan panjang untuk membuat rentang yang sama adalah
let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
Anda dapat melihat bahwa tipe indeks di sini adalah Int
. Itu tidak berhasil String
, karena String terbuat dari Karakter dan tidak semua karakter memiliki ukuran yang sama. (Baca ini untuk info lebih lanjut.) Emoji seperti 😀, misalnya, membutuhkan lebih banyak tempat daripada huruf "b".
Masalah dengan NSRange
Cobalah bereksperimen dengan NSRange
dan NSString
dengan emoji dan Anda akan melihat apa yang saya maksud. Sakit kepala.
let myNSRange = NSRange(location: 1, length: 3)
let myNSString: NSString = "abcde"
myNSString.substring(with: myNSRange) // "bcd"
let myNSString2: NSString = "a😀cde"
myNSString2.substring(with: myNSRange) // "😀c" Where is the "d"!?
Wajah tersenyum membutuhkan dua unit kode UTF-16 untuk disimpan, sehingga memberikan hasil yang tidak diharapkan dari tidak menyertakan "d".
Solusi Cepat
Karena itu, dengan Swift Strings yang Anda gunakan Range<String.Index>
, bukan Range<Int>
. Indeks String dihitung berdasarkan string tertentu sehingga mengetahui apakah ada emoji atau cluster grafik yang diperluas.
Contoh
var myString = "abcde"
let start = myString.index(myString.startIndex, offsetBy: 1)
let end = myString.index(myString.startIndex, offsetBy: 4)
let myRange = start..<end
myString[myRange] // "bcd"
myString = "a😀cde"
let start2 = myString.index(myString.startIndex, offsetBy: 1)
let end2 = myString.index(myString.startIndex, offsetBy: 4)
let myRange2 = start2..<end2
myString[myRange2] // "😀cd"
a...
dan ...b
dan..<b
Di Swift 4 hal disederhanakan sedikit. Kapan pun titik awal atau akhir suatu rentang dapat disimpulkan, Anda dapat membiarkannya.
Int
Anda dapat menggunakan rentang integer satu sisi untuk mengulang koleksi. Berikut beberapa contoh dari dokumentasinya .
// iterate from index 2 to the end of the array
for name in names[2...] {
print(name)
}
// iterate from the beginning of the array to index 2
for name in names[...2] {
print(name)
}
// iterate from the beginning of the array up to but not including index 2
for name in names[..<2] {
print(name)
}
// the range from negative infinity to 5. You can't iterate forward
// over this because the starting point in unknown.
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
// You can iterate over this but it will be an infinate loop
// so you have to break out at some point.
let range = 5...
Tali
Ini juga berfungsi dengan rentang String. Jika Anda membuat rentang dengan str.startIndex
atau str.endIndex
di salah satu ujung, Anda bisa membiarkannya. Kompilator akan menyimpulkannya.
Diberikan
var str = "Hello, playground"
let index = str.index(str.startIndex, offsetBy: 5)
let myRange = ..<index // Hello
Anda dapat beralih dari indeks ke str.endIndex dengan menggunakan ...
var str = "Hello, playground"
let index = str.index(str.endIndex, offsetBy: -10)
let myRange = index... // playground
Catatan
NSString
menyimpan karakternya secara internal dalam pengkodean UTF-16. Skalar unicode penuh adalah 21-bit. Karakter wajah yang menyeringai ( U+1F600
) tidak dapat disimpan dalam satu unit kode 16-bit, sehingga tersebar 2. NSRange
dihitung berdasarkan unit kode 16-bit. Dalam contoh ini, 3 unit kode kebetulan hanya mewakili 2 karakter
Xcode 8 beta 2 • Swift 3
let myString = "Hello World"
let myRange = myString.startIndex..<myString.index(myString.startIndex, offsetBy: 5)
let mySubString = myString.substring(with: myRange) // Hello
Xcode 7 • Swift 2.0
let myString = "Hello World"
let myRange = Range<String.Index>(start: myString.startIndex, end: myString.startIndex.advancedBy(5))
let mySubString = myString.substringWithRange(myRange) // Hello
atau sederhananya
let myString = "Hello World"
let myRange = myString.startIndex..<myString.startIndex.advancedBy(5)
let mySubString = myString.substringWithRange(myRange) // Hello
Gunakan seperti ini
var start = str.startIndex // Start at the string's start index
var end = advance(str.startIndex, 5) // Take start index and advance 5 characters forward
var range: Range<String.Index> = Range<String.Index>(start: start,end: end)
let firstFiveDigit = str.substringWithRange(range)
print(firstFiveDigit)
Hasil: Halo
Saya merasa mengejutkan bahwa, bahkan di Swift 4, masih tidak ada cara asli sederhana untuk mengekspresikan rentang String menggunakan Int. Satu-satunya metode String yang memungkinkan Anda menyediakan Int sebagai cara untuk mendapatkan substring menurut range adalah prefix
dan suffix
.
Berguna untuk memiliki beberapa utilitas konversi, sehingga kita dapat berbicara seperti NSRange saat berbicara dengan String. Berikut adalah utilitas yang mengambil lokasi dan panjang, seperti NSRange, dan mengembalikan Range<String.Index>
:
func range(_ start:Int, _ length:Int) -> Range<String.Index> {
let i = self.index(start >= 0 ? self.startIndex : self.endIndex,
offsetBy: start)
let j = self.index(i, offsetBy: length)
return i..<j
}
Misalnya, "hello".range(0,1)"
adalah Range<String.Index>
merangkul karakter pertama "hello"
. Sebagai bonus, saya mengizinkan lokasi negatif: "hello".range(-1,1)"
adalah Range<String.Index>
pelukan karakter terakhir "hello"
.
Berguna juga untuk mengonversi a Range<String.Index>
menjadi NSRange, untuk saat-saat ketika Anda harus berbicara dengan Cocoa (misalnya, dalam menangani rentang atribut NSAttributedString). Swift 4 menyediakan cara asli untuk melakukan itu:
let nsrange = NSRange(range, in:s) // where s is the string
Dengan demikian kita dapat menulis utilitas lain di mana kita pergi langsung dari lokasi dan panjang String ke NSRange:
extension String {
func nsRange(_ start:Int, _ length:Int) -> NSRange {
return NSRange(self.range(start,length), in:self)
}
}
Saya membuat ekstensi berikut:
extension String {
func substring(from from:Int, to:Int) -> String? {
if from<to && from>=0 && to<self.characters.count {
let rng = self.startIndex.advancedBy(from)..<self.startIndex.advancedBy(to)
return self.substringWithRange(rng)
} else {
return nil
}
}
}
contoh penggunaan:
print("abcde".substring(from: 1, to: 10)) //nil
print("abcde".substring(from: 2, to: 4)) //Optional("cd")
print("abcde".substring(from: 1, to: 0)) //nil
print("abcde".substring(from: 1, to: 1)) //nil
print("abcde".substring(from: -1, to: 1)) //nil
Anda bisa menggunakan seperti ini
let nsRange = NSRange(location: someInt, length: someInt)
seperti dalam
let myNSString = bigTOTPCode as NSString //12345678
let firstDigit = myNSString.substringWithRange(NSRange(location: 0, length: 1)) //1
let secondDigit = myNSString.substringWithRange(NSRange(location: 1, length: 1)) //2
let thirdDigit = myNSString.substringWithRange(NSRange(location: 2, length: 4)) //3456
Aku ingin melakukan ini:
print("Hello"[1...3])
// out: Error
Tapi sayangnya, saya tidak bisa menulis subskrip saya sendiri karena yang dibenci mengambil ruang nama.
Namun kami dapat melakukan ini:
print("Hello"[range: 1...3])
// out: ell
Cukup tambahkan ini ke proyek Anda:
extension String {
subscript(range: ClosedRange<Int>) -> String {
get {
let start = String.Index(utf16Offset: range.lowerBound, in: self)
let end = String.Index(utf16Offset: range.upperBound, in: self)
return String(self[start...end])
}
}
}
Range<String.Index>
, tapi terkadang perlu untuk bekerja denganNSString
danNSRange
, jadi beberapa konteks lainnya akan berguna. - Tapi lihat stackoverflow.com/questions/24092884/… .