String.Index Swift'de nasıl çalışır?


109

Bazı eski kodumu ve cevaplarımı Swift 3 ile güncelliyordum, ancak Swift Strings ve Indexing'e geldiğimde bazı şeyleri anlamak çok zor oldu.

Özellikle şunları deniyordum:

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

ikinci satır bana şu hatayı veriyordu

'advancedBy' kullanılamıyor: Bir dizini n adım ilerletmek için, dizini oluşturan CharacterView örneğinde 'index (_: offsetBy :)' çağrısı yapın.

Bunun Stringaşağıdaki yöntemlere sahip olduğunu görüyorum .

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)

Bunlar ilk başta gerçekten kafamı karıştırıyordu, bu yüzden onları anlayana kadar onlarla oynamaya başladım. Nasıl kullanıldıklarını göstermek için aşağıya bir cevap ekliyorum.

Yanıtlar:


282

görüntü açıklamasını buraya girin

Aşağıdaki örneklerin tümü

var str = "Hello, playground"

startIndex ve endIndex

  • startIndex ilk karakterin dizini
  • endIndexendeksidir sonra son karakteri.

Misal

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

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

Swift 4'ün tek taraflı menzilleri ile menzil, aşağıdaki formlardan birine basitleştirilebilir.

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

Açıklık sağlamak için aşağıdaki örneklerde tam formu kullanacağım, ancak okunabilirlik açısından muhtemelen kodunuzda tek taraflı aralıkları kullanmak isteyeceksiniz.

after

De olduğu gibi: index(after: String.Index)

  • after verilen dizinden hemen sonraki karakterin dizinini ifade eder.

Örnekler

// 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

De olduğu gibi: index(before: String.Index)

  • before verilen dizinden hemen önceki karakterin dizinini ifade eder.

Örnekler

// 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

De olduğu gibi: index(String.Index, offsetBy: String.IndexDistance)

  • offsetByDeğer verilen dizinden pozitif veya negatif ve başlar olabilir. Türünde olmasına rağmen, String.IndexDistancebir Int.

Örnekler

// 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

De olduğu gibi: index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)

  • limitedByEmin ofset endeks sınırların dışında gitmek neden olmaması yapmak için yararlıdır. Sınırlayıcı bir indekstir. Ofsetin sınırı aşması mümkün olduğundan, bu yöntem bir İsteğe Bağlı döndürür. nilEndeks sınırların dışındaysa geri döner .

Misal

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

Offset 77yerine olsaydı 7, ififade atlanırdı.

String.Index neden gereklidir?

Dizeler için bir dizin kullanmak çok daha kolay olurdu Int. String.IndexHer String için yeni bir tane oluşturmanız gerekmesinin nedeni, Swift'deki Karakterlerin başlık altında aynı uzunlukta olmamasıdır. Tek bir Swift Karakteri, bir, iki veya daha fazla Unicode kod noktasından oluşabilir. Bu nedenle, her benzersiz String, Karakterlerinin dizinlerini hesaplamalıdır.

Bu karmaşıklığı bir Int dizin uzantısının arkasına gizlemek olasıdır, ancak bunu yapmakta isteksizim. Gerçekte ne olduğunun hatırlatılması güzel.


18
Neden startIndex0'dan başka bir şey olsun ki?
Robo Robok

15
@RoboRobok: Swift, "grafem kümelerinden" oluşan Unicode karakterleriyle çalıştığından, Swift dizin konumlarını temsil etmek için tamsayılar kullanmaz. Diyelim ki ilk karakteriniz bir é. Aslında eartı bir \u{301}Unicode gösteriminden yapılmıştır. Sıfırlık bir dizin kullanırsanız, eya da vurgu ("ciddi") karakterini alırsınız é,. Kullanmak, startIndexherhangi bir karakter için tüm grapheme kümesini elde etmenizi sağlar.
leanne

2
Swift 4.0'da her Unicode karakteri 1 ile sayılır. Örn: "👩‍💻" .count // Şimdi: 1, Önce: 2
selva

3
String.IndexBir tamsayıdan, bir kukla dizge oluşturmak ve .indexonun üzerindeki yöntemi kullanmaktan başka, bir kişi nasıl bir a oluşturur ? Bir şeyi mi kaçırdığımı bilmiyorum ama doktorlar hiçbir şey söylemiyor.
sudo

3
@sudo, String.Indexbir tamsayı ile a oluştururken biraz dikkatli olmalısın çünkü her Swift Character, bir tamsayı ile kastettiğin aynı şeye eşit olmayabilir. Bununla birlikte, bir offsetByoluşturmak için parametreye bir tamsayı geçirebilirsiniz String.Index. Eğer bir a sahip Stringdeğilseniz, o zaman bir a oluşturamazsınız String.Index(çünkü Swift dizini yalnızca dizedeki önceki karakterlerin ne olduğunu biliyorsa hesaplayabilir). Dizeyi değiştirirseniz, dizini yeniden hesaplamanız gerekir. Aynısını String.Indexiki farklı dizede kullanamazsınız .
Suragch

0
func change(string: inout String) {

    var character: Character = .normal

    enum Character {
        case space
        case newLine
        case normal
    }

    for i in stride(from: string.count - 1, through: 0, by: -1) {
        // first get index
        let index: String.Index?
        if i != 0 {
            index = string.index(after: string.index(string.startIndex, offsetBy: i - 1))
        } else {
            index = string.startIndex
        }

        if string[index!] == "\n" {

            if character != .normal {

                if character == .newLine {
                    string.remove(at: index!)
                } else if character == .space {
                    let number = string.index(after: string.index(string.startIndex, offsetBy: i))
                    if string[number] == " " {
                        string.remove(at: number)
                    }
                    character = .newLine
                }

            } else {
                character = .newLine
            }

        } else if string[index!] == " " {

            if character != .normal {

                string.remove(at: index!)

            } else {
                character = .space
            }

        } else {

            character = .normal

        }

    }

    // startIndex
    guard string.count > 0 else { return }
    if string[string.startIndex] == "\n" || string[string.startIndex] == " " {
        string.remove(at: string.startIndex)
    }

    // endIndex - here is a little more complicated!
    guard string.count > 0 else { return }
    let index = string.index(before: string.endIndex)
    if string[index] == "\n" || string[index] == " " {
        string.remove(at: index)
    }

}

-2

TableViewController'ın içinde bir UITextView oluşturun. Ben function: textViewDidChange kullandım ve sonra return-key-input için kontrol ettim. daha sonra dönüş tuşu girişi algılarsa, dönüş tuşunun girişini silin ve klavyeyi kapatın.

func textViewDidChange(_ textView: UITextView) {
    tableView.beginUpdates()
    if textView.text.contains("\n"){
        textView.text.remove(at: textView.text.index(before: textView.text.endIndex))
        textView.resignFirstResponder()
    }
    tableView.endUpdates()
}
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.