Swift'de “Index” i “Int” türüne nasıl dönüştürülür?


91

Bir dizede bulunan bir harfin dizinini tamsayı değerine dönüştürmek istiyorum. Başlık dosyalarını okumaya çalıştım Index, ancak ForwardIndexTypeyöntemlerle protokole uygun görünmesine rağmen türünü bulamıyorum (örn. distanceTo).

var letters = "abcdefg"
let index = letters.characters.indexOf("c")!

// ERROR: Cannot invoke initializer for type 'Int' with an argument list of type '(String.CharacterView.Index)'
let intValue = Int(index)  // I want the integer value of the index (e.g. 2)

Herhangi bir yardım takdir edilmektedir.


xcode 7.2 ve swift 2.x'iniz var mı?
aaisataev

Aslında şu anda Xcode 7.2'yi indiriyorum.
Christopher

33
Bir oyun alanında yüzünüze bakmak istediğiniz dizini görmekten daha sinir bozucu bir şey yoktur ve dizini ihtiyacınız olan türe dönüştürmek devasa bir PITA'dır. Ağaçkakanı bir kapıya çarpmayı tercih ederim.
Adrian

1
Swift 3: let index = letters.characters.index(of: "c") Sonraki Satırlet int_index = letters.characters.distance(from: letters.startIndex, to: index)
Viktor

8
Apple WTF !!!!!!
Borzh

Yanıtlar:


87

düzenle / güncelle:

Xcode 11 • Swift 5.1 veya üzeri

extension StringProtocol {
    func distance(of element: Element) -> Int? { firstIndex(of: element)?.distance(in: self) }
    func distance<S: StringProtocol>(of string: S) -> Int? { range(of: string)?.lowerBound.distance(in: self) }
}

extension Collection {
    func distance(to index: Index) -> Int { distance(from: startIndex, to: index) }
}

extension String.Index {
    func distance<S: StringProtocol>(in string: S) -> Int { string.distance(to: self) }
}

Oyun alanı testi

let letters = "abcdefg"

let char: Character = "c"
if let distance = letters.distance(of: char) {
    print("character \(char) was found at position #\(distance)")   // "character c was found at position #2\n"
} else {
    print("character \(char) was not found")
}

let string = "cde"
if let distance = letters.distance(of: string) {
    print("string \(string) was found at position #\(distance)")   // "string cde was found at position #2\n"
} else {
    print("string \(string) was not found")
}

48
Neden bir dizinin elemanının tamsayı-değer indeksini döndüren bir fonksiyon yapmaya karar vermedikleri kafam karıştı .. smh
MarksCode

6
Tüm karakterler tek bir baytta saklanamaz. Biraz zaman ayırıp Swift String belgelerini
okumalısınız


4
Tbh Bir dizinin değil, normal bir dizinin elemanının tamsayı-değer indeksini almaya çalışıyorum.
MarksCode

28
Basit şeyleri gereksiz yere karmaşık hale getirmek :(
Iulian Onofrei

4

Swift 4

var str = "abcdefg"
let index = str.index(of: "c")?.encodedOffset // Result: 2

Not: Dize aynı çoklu karakter içeriyorsa, soldan en yakın olanı alır.

var str = "abcdefgc"
let index = str.index(of: "c")?.encodedOffset // Result: 2

6
EncodedOffset kullanmayın. encodedOffset kullanımdan kaldırıldı: encodedOffset, en yaygın kullanım yanlış olduğu için kullanımdan kaldırıldı. deneyin"🇺🇸🇺🇸🇧🇷".index(of: "🇧🇷")?.encodedOffset // 16
Leo Dabus

@LeoDabus, kullanımdan kaldırıldığını doğru bir şekilde belirtiyorsunuz. Ama yine de bir cevap olarak kullanmayı öneriyorsun. Doğru cevap: index_Of_YourStringVariable.utf16Offset (in: yourStringVariable)
TDesign

Hayır. UTF16 ofset muhtemelen istediğiniz şey değil
Leo Dabus

1

encodedOffsetSwift 4.2'den kaldırıldı .

Kullanımdan kaldırma mesajı: encodedOffsetEn yaygın kullanım yanlış olduğu için kullanımdan kaldırıldı. utf16Offset(in:)Aynı davranışı elde etmek için kullanın .

Böylece şu şekilde kullanabiliriz utf16Offset(in:):

var str = "abcdefgc"
let index = str.index(of: "c")?.utf16Offset(in: str) // Result: 2

1
deneyin let str = "🇺🇸🇺🇸🇧🇷" let index = str.firstIndex(of: "🇧🇷")?.utf16Offset(in: str)// Sonuç: 8
Leo Dabus

0

Dizine dayalı dizge işlemi gerçekleştirmek için, bunu geleneksel dizin sayısal yaklaşımı ile yapamazsınız. çünkü swift.index, indices işlevi tarafından alınır ve Int türünde değildir. String bir karakter dizisi olmasına rağmen, yine de indekse göre öğeyi okuyamıyoruz.

Bu sinir bozucu.

Bu nedenle, dizenin her çift karakterinden yeni bir alt dize oluşturmak için aşağıdaki kodu kontrol edin.

let mystr = "abcdefghijklmnopqrstuvwxyz"
let mystrArray = Array(mystr)
let strLength = mystrArray.count
var resultStrArray : [Character] = []
var i = 0
while i < strLength {
    if i % 2 == 0 {
        resultStrArray.append(mystrArray[i])
      }
    i += 1
}
let resultString = String(resultStrArray)
print(resultString)

Çıktı: acegikmoqsuwy

Şimdiden teşekkürler


bu, filtre ile kolayca gerçekleştirilebilirvar counter = 0 let result = mystr.filter { _ in defer { counter += 1 } return counter.isMultiple(of: 2) }
Leo Dabus

String.index'i kullanmayı tercih ediyorsanızvar index = mystr.startIndex let result = mystr.filter { _ in defer { mystr.formIndex(after: &index) } return mystr.distance(from: mystr.startIndex, to: index).isMultiple(of: 2) }
Leo Dabus

0

Aşağıda, bir alt dizenin sınırlarına değerler Intyerine s olarak erişmenizi sağlayacak bir uzantı verilmiştir String.Index:

import Foundation

/// This extension is available at
/// https://gist.github.com/zackdotcomputer/9d83f4d48af7127cd0bea427b4d6d61b
extension StringProtocol {
    /// Access the range of the search string as integer indices
    /// in the rendered string.
    /// - NOTE: This is "unsafe" because it may not return what you expect if
    ///     your string contains single symbols formed from multiple scalars.
    /// - Returns: A `CountableRange<Int>` that will align with the Swift String.Index
    ///     from the result of the standard function range(of:).
    func countableRange<SearchType: StringProtocol>(
        of search: SearchType,
        options: String.CompareOptions = [],
        range: Range<String.Index>? = nil,
        locale: Locale? = nil
    ) -> CountableRange<Int>? {
        guard let trueRange = self.range(of: search, options: options, range: range, locale: locale) else {
            return nil
        }

        let intStart = self.distance(from: startIndex, to: trueRange.lowerBound)
        let intEnd = self.distance(from: trueRange.lowerBound, to: trueRange.upperBound) + intStart

        return Range(uncheckedBounds: (lower: intStart, upper: intEnd))
    }
}

Bunun tuhaflığa yol açabileceğinin farkında olun, bu yüzden Apple bunu zorlaştırmayı seçti. (Bu tartışmalı bir tasarım kararı olsa da - tehlikeli bir şeyi sadece zorlaştırarak gizlemek ...)

Apple'ın String belgelerinde daha fazlasını okuyabilirsiniz , ancak tldr, bu "indekslerin" aslında uygulamaya özgü olmasından kaynaklanıyor olmasıdır. İşletim sistemi tarafından oluşturulduktan sonra dizeye dizinleri temsil ederler ve bu nedenle, kullanılan Unicode spesifikasyonunun sürümüne bağlı olarak işletim sisteminden işletim sistemine geçiş yapabilirler. Bu, dizide doğru yeri belirlemek için UTF spesifikasyonunun veriler üzerinde çalıştırılması gerektiğinden, dizine göre değerlere erişmenin artık sabit zamanlı bir işlem olmadığı anlamına gelir. Bu endeksler ayrıca NSString tarafından oluşturulan değerlerle, eğer ona köprü kurarsanız veya temeldeki UTF skalerlerinin indeksleriyle aynı çizgide olmayacaktır. Uyarı geliştirici.


startIndex'ten mesafeyi tekrar ölçmeye gerek yoktur. Sadece aşağıdan yukarıya olan mesafeyi ölçün ve başlangıcı ekleyin.
Leo Dabus

Yönteminize seçenekleri de eklerdim. Gibi bir şeyfunc rangeInt<S: StringProtocol>(of aString: S, options: String.CompareOptions = []) -> Range<Int>? { guard let range = range(of: aString, options: options) else { return nil } let start = distance(from: startIndex, to: range.lowerBound) return start..<start+distance(from: range.lowerBound, to: range.upperBound) }
Leo Dabus

Muhtemelen yöntemin adını değiştirip countableRangegeri döneceğimCountableRange
Leo Dabus

Her yerde iyi öneriler @LeoDabus. Aslında tüm range (of ...) argümanlarını ekledim, böylece artık countableRange (of :, options :, range:, locale :) 'yi çağırabilirsiniz. Gist'i güncellediniz, bu gönderiyi de güncelleyecektir.
Zack

Bu yöntemi bir alt
dizede
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.