Yanıtlar:
Swift 4 için güncellendi
Swift aralıkları daha karmaşıktır NSRangeve bu karmaşıklığın bazı arkasındaki mantığı anlamaya çalışmak isterlerse Swift 3'te daha kolay alamadım, okumak bu ve bu . Size onları nasıl yaratacağınızı ve ne zaman kullanabileceğinizi göstereceğim.
a...bBu aralık operatör eleman içerir Swift aralığı oluşturur a ve eleman bolsa bile, bbir tür (gibi mümkün olan en yüksek değer Int.max). İki farklı kapalı aralık türü vardır: ClosedRangeve CountableClosedRange.
ClosedRangeSwift'deki tüm aralıkların öğeleri karşılaştırılabilir (yani, Karşılaştırılabilir protokole uygundurlar). Bu, bir koleksiyondaki aralıktaki öğelere erişmenizi sağlar. İşte bir örnek:
let myRange: ClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
Bununla birlikte, a ClosedRangesayılamaz (yani, Sıra protokolüne uymaz). Bu, bir fordöngü ile öğeler üzerinde yineleme yapamayacağınız anlamına gelir . Bunun için CountableClosedRange.
CountableClosedRangeBu, sonuncusuna benzer, ancak artık aralık da yinelenebilir.
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..<bBu aralık operatör eleman içerir aama değil elemanı b. Yukarıdaki gibi, iki farklı türde yarı açık aralık vardır: Rangeve CountableRange.
RangeOlduğu gibi ClosedRange, bir koleksiyonun öğelerine bir Range. Misal:
let myRange: Range = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
Yine de, a üzerinde yineleyemezsiniz Rangeçünkü bu sadece karşılaştırılabilir, zorlu değil.
CountableRangeA CountableRangeyinelemeye izin verir.
let myRange: CountableRange = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
for index in myRange {
print(myArray[index])
}
Yine NSRangede Swift'i bazen kullanabilirsin (gerekir) ( örneğin, atıfta bulunulan dizeleri yaparken ), bu yüzden nasıl yapılacağını bilmek yardımcı olur.
let myNSRange = NSRange(location: 3, length: 2)
Bunun konum ve uzunluk olduğunu , başlangıç dizini ve bitiş dizini olmadığını unutmayın. Buradaki örnek, Swift serisi ile benzerdir 3..<5. Ancak tipler farklı olduğu için birbirlerinin yerine kullanılamazlar.
...Ve ..<aralık operatörleri aralıkları oluşturmak bir kısa yoludur. Örneğin:
let myRange = 1..<3
Aynı aralığı yaratmanın uzun yolu,
let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
Burada indeks tipinin olduğunu görebilirsiniz Int. Ancak bu işe yaramaz String, çünkü Dizeler Karakterlerden yapılmıştır ve tüm karakterler aynı boyutta değildir. ( Daha fazla bilgi için bunu okuyun .) Örneğin 😀 gibi bir emoji, "b" harfinden daha fazla yer kaplar.
NSRange ile ilgili sorun
NSRangeVe bir NSStringemoji ile denemeyi deneyin ve ne demek istediğimi göreceksiniz. Baş ağrısı.
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"!?
Gülen yüz, saklamak için iki UTF-16 kod birimi alır, bu nedenle "d" harfinin dahil edilmemesi gibi beklenmedik bir sonuç verir.
Hızlı Çözüm
Bu nedenle, Swift Strings ile Kullanmak Range<String.Index>değil Range<Int>. Dize Dizini, belirli bir dizeye göre hesaplanır, böylece herhangi bir emoji veya genişletilmiş grafik kümeleri olup olmadığını bilir.
Misal
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...ve ...bve..<bSwift 4'te işler biraz basitleştirildi. Bir aralığın başlangıç veya bitiş noktası çıkarıldığında, onu kapalı bırakabilirsiniz.
Int
Koleksiyonları yinelemek için tek taraflı tam sayı aralıkları kullanabilirsiniz. İşte bazı örnekler belgeler .
// 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...
Dize
Bu aynı zamanda String aralıklarıyla da çalışır. Bir ucuyla str.startIndexveya str.endIndexbir uçta bir aralık oluşturuyorsanız , onu kapalı bırakabilirsiniz. Derleyici bunu çıkaracaktır.
Verilen
var str = "Hello, playground"
let index = str.index(str.startIndex, offsetBy: 5)
let myRange = ..<index // Hello
Kullanarak dizinden str.endIndex'e gidebilirsiniz. ...
var str = "Hello, playground"
let index = str.index(str.endIndex, offsetBy: -10)
let myRange = index... // playground
Notlar
NSStringkarakterlerini UTF-16 kodlamasında dahili olarak saklar. Tam bir unicode skaler 21 bittir. Sırıtan yüz karakteri ( U+1F600) tek bir 16 bit kod biriminde saklanamaz, bu nedenle NSRange16 bitlik kod birimlerine dayalı 2 sayıya yayılır . Bu örnekte, 3 kod birimi yalnızca 2 karakteri temsil etmektedir
Xcode 8 beta 2 • Hızlı 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
ya da sadece
let myString = "Hello World"
let myRange = myString.startIndex..<myString.startIndex.advancedBy(5)
let mySubString = myString.substringWithRange(myRange) // Hello
Bunun gibi kullan
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)
Çıktı: Merhaba
Swift 4'te bile, Int kullanarak bir String aralığını ifade etmenin basit bir yerel yolu olmadığını şaşırtıcı buluyorum. Aralığa göre bir alt dizeyi elde etmenin bir yolu olarak bir Int sağlamanıza izin veren tek String yöntemleri prefixve suffix.
Bir String ile konuşurken NSRange gibi konuşabilmemiz için elinizde bazı dönüştürme araçlarına sahip olmak yararlıdır. İşte NSRange gibi bir konum ve uzunluk alan ve bir döndüren bir yardımcı program 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
}
Örneğin, "hello".range(0,1)"bir Range<String.Index>ilk karakterini kucaklayan "hello". Bir bonus olarak, negatif yerler izin verdiğiniz: "hello".range(-1,1)"edilmektedir Range<String.Index>son karakterini kucaklayan "hello".
Range<String.Index>Cocoa ile konuşmanız gereken anlar için (örneğin, NSAttributedString öznitelik aralıklarıyla uğraşırken) a'yı NSRange'ye dönüştürmek de yararlıdır . Swift 4, bunu yapmanın yerel bir yolunu sağlar:
let nsrange = NSRange(range, in:s) // where s is the string
Böylece, doğrudan bir String konumundan ve uzunluğundan bir NSRange'a gittiğimiz başka bir yardımcı program yazabiliriz:
extension String {
func nsRange(_ start:Int, _ length:Int) -> NSRange {
return NSRange(self.range(start,length), in:self)
}
}
Aşağıdaki uzantıyı oluşturdum:
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
}
}
}
kullanım örneği:
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
Bunun gibi kullanabilirsin
let nsRange = NSRange(location: someInt, length: someInt)
de olduğu gibi
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
Bunu yapmak istiyorum:
print("Hello"[1...3])
// out: Error
Ama maalesef kendime ait bir alt simge yazamam çünkü nefret edilen kişi isim alanını kaplıyor.
Ancak bunu yapabiliriz:
print("Hello"[range: 1...3])
// out: ell
Bunu projenize eklemeniz yeterlidir:
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>, ancak bazenNSStringve ile çalışmak gerekirNSRange, bu nedenle biraz daha fazla bağlam yararlı olabilir. - Ancak stackoverflow.com/questions/24092884/… adresine bir göz atın .