Yanıtlar:
Swift 4 için güncellendi
Swift aralıkları daha karmaşıktır NSRange
ve 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...b
Bu aralık operatör eleman içerir Swift aralığı oluşturur a
ve eleman b
olsa bile, b
bir tür (gibi mümkün olan en yüksek değer Int.max
). İki farklı kapalı aralık türü vardır: ClosedRange
ve CountableClosedRange
.
ClosedRange
Swift'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 ClosedRange
sayılamaz (yani, Sıra protokolüne uymaz). Bu, bir for
döngü ile öğeler üzerinde yineleme yapamayacağınız anlamına gelir . Bunun için CountableClosedRange
.
CountableClosedRange
Bu, 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..<b
Bu aralık operatör eleman içerir a
ama değil elemanı b
. Yukarıdaki gibi, iki farklı türde yarı açık aralık vardır: Range
ve CountableRange
.
Range
Olduğ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.
CountableRange
A CountableRange
yinelemeye 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 NSRange
de 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
NSRange
Ve bir NSString
emoji 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 ...b
ve..<b
Swift 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.startIndex
veya str.endIndex
bir 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
NSString
karakterlerini 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 NSRange
16 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 prefix
ve 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 bazenNSString
ve ile çalışmak gerekirNSRange
, bu nedenle biraz daha fazla bağlam yararlı olabilir. - Ancak stackoverflow.com/questions/24092884/… adresine bir göz atın .