Swift'de aralık nasıl oluşturulur?


104

Objective-c'de NSRange kullanarak aralık oluşturuyoruz

NSRange range;

Peki Swift'de menzil nasıl oluşturulur?


3
Menzile ne için ihtiyacınız var? Swift dizeleri kullanır Range<String.Index>, ancak bazen NSStringve ile çalışmak gerekir NSRange, bu nedenle biraz daha fazla bağlam yararlı olabilir. - Ancak stackoverflow.com/questions/24092884/… adresine bir göz atın .
Martin R

Yanıtlar:


258

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.

Kapalı Aralıklar: a...b

Bu 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.

1. 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 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.

2. 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])
}

Yarı Açık Aralıklar: a..<b

Bu 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.

1. 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.

2. CountableRange

A 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])
}

NSRange

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.

Dizelerle Aralıklar

...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"

Tek Taraflı Aralıklar: a...ve ...bve..<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.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

Ayrıca bakınız:

Notlar

  • Tek bir dizeyle oluşturduğunuz bir aralığı farklı bir dizede kullanamazsınız.
  • Gördüğünüz gibi, String aralıkları Swift'de bir sıkıntıdır, ancak emoji ve diğer Unicode skalarlarıyla daha iyi başa çıkmayı mümkün kılarlar.

İlerideki çalışma


Yorumum "NSRange ile İlgili Sorun" alt bölümü ile ilgilidir. 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
Farklı Kod

25

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


2

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


"Aralık" Veri Tiplerine sahip olduklarını görüyorum. Peki bunu nasıl kullanabilirim?
vichhai

@vichhai Nerede kullanmak istediğinizi daha fazla detaylandırabilir misiniz?
Dharmbir Singh

Hedef c: NSRange aralığı gibi;
vichhai

1

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)
    }
}

1

NSRange nesnesi oluşturmak isteyen biri şu şekilde oluşturabilir:

let range: NSRange = NSRange.init(location: 0, length: 5)

bu, konum 0 ve uzunluk 5 olan aralık oluşturacaktır


1
func replace(input: String, start: Int,lenght: Int, newChar: Character) -> String {
    var chars = Array(input.characters)

    for i in start...lenght {
        guard i < input.characters.count else{
            break
        }
        chars[i] = newChar
    }
    return String(chars)
}

0

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

0

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

0

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])
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.