Come convertire "Indice" per digitare "Int" in Swift?


90

Voglio convertire l'indice di una lettera contenuta in una stringa in un valore intero. Ho tentato di leggere i file di intestazione ma non riesco a trovare il tipo per Index, sebbene sembri conforme al protocollo ForwardIndexTypecon metodi (ad esempio 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)

Qualsiasi aiuto è apprezzato.


hai xcode 7.2 e swift 2.x?
— aaisataev

In realtà, sto scaricando Xcode 7.2 proprio in questo momento.
— Christopher

33
Non c'è niente di più frustrante che vedere l'indice che vuoi fissarti in faccia in un parco giochi ed è un gigantesco PITA per convertire l'indice nel tipo di cui hai bisogno. Preferisco sbattere il mio uccello in una porta.
— Adrian

1
Swift 3: let index = letters.characters.index(of: "c") Next Linelet int_index = letters.characters.distance(from: letters.startIndex, to: index)
— Viktor

8
Apple WTF !!!!!!
— Borzh

Risposte:


87

modifica / aggiorna:

Xcode 11 • Swift 5.1 o successivo

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

Test del parco giochi

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

47
Sono così confuso perché non avrebbero deciso di creare una funzione che restituisse l'indice del valore intero di un elemento di un array .. smh
— MarksCode

6
Non tutti i caratteri possono essere memorizzati in un singolo byte. Dovresti prenderti un po 'di tempo e leggere la documentazione di Swift String
— Leo Dabus


4
Tbh Sto cercando di ottenere l'indice del valore intero di un elemento di un array regolare, non una stringa.
— MarksCode

28
Rendere le cose semplici inutilmente complicate :(
— Iulian Onofrei

4

Swift 4

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

Nota: se la stringa contiene più caratteri, otterrà solo quello più vicino da sinistra

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

5
Non utilizzare encodedOffset. encodedOffset è deprecato: encodedOffset è stato deprecato poiché l'utilizzo più comune non è corretto. prova"🇺🇸🇺🇸🇧🇷".index(of: "🇧🇷")?.encodedOffset // 16
— Leo Dabus

@LeoDabus stai affermando correttamente che è deprecato. Ma stai comunque suggerendo di usarlo come risposta. La risposta corretta è: index_Of_YourStringVariable.utf16Offset (in: yourStringVariable)
— TDesign

No. L'offset UTF16 probabilmente non è quello che vuoi
— Leo Dabus il

1

encodedOffsetè stato deprecato da Swift 4.2 .

Messaggio di deprecazione : encodedOffsetè stato deprecato poiché l'utilizzo più comune non è corretto. Usalo utf16Offset(in:)per ottenere lo stesso comportamento.

Quindi possiamo usare in utf16Offset(in:)questo modo:

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

1
prova let str = "🇺🇸🇺🇸🇧🇷" let index = str.firstIndex(of: "🇧🇷")?.utf16Offset(in: str)// Risultato: 8
— Leo Dabus

0

Per eseguire un'operazione sulle stringhe basata sull'indice, non è possibile farlo con l'approccio numerico dell'indice tradizionale. perché swift.index viene recuperato dalla funzione indici e non è nel tipo Int. Anche se String è un array di caratteri, non possiamo ancora leggere elemento per indice.

Questo è frustrante.

Quindi, per creare una nuova sottostringa di ogni carattere pari della stringa, controlla il codice sottostante.

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)

Uscita: acegikmoqsuwy

Grazie in anticipo


questo può essere facilmente ottenuto con il filtrovar counter = 0 let result = mystr.filter { _ in defer { counter += 1 } return counter.isMultiple(of: 2) }
— Leo Dabus

se preferisci usare String.indexvar 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

Ecco un'estensione che ti permetterà di accedere ai limiti di una sottostringa come Ints invece che come String.Indexvalori:

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

Basta essere consapevoli che questo può portare a stranezze, motivo per cui Apple ha scelto di renderlo difficile. (Anche se è una decisione progettuale discutibile: nascondere una cosa pericolosa semplicemente rendendola difficile ...)

Puoi leggere di più nella documentazione String di Apple , ma il tldr è che deriva dal fatto che questi "indici" sono in realtà specifici dell'implementazione. Rappresentano gli indici nella stringa dopo che è stata renderizzata dal sistema operativo e quindi possono passare da sistema operativo a sistema operativo a seconda della versione della specifica Unicode utilizzata. Ciò significa che l'accesso ai valori in base all'indice non è più un'operazione a tempo costante, perché la specifica UTF deve essere eseguita sui dati per determinare il posto giusto nella stringa. Questi indici inoltre non si allineeranno con i valori generati da NSString, se si collega ad esso, o con gli indici negli scalari UTF sottostanti. Sviluppatore avvertimento.


non è necessario misurare nuovamente la distanza dallo startIndex. Prendi la distanza dal basso verso l'alto e aggiungi l'inizio.
— Leo Dabus il

Aggiungerei anche le opzioni al tuo metodo. Qualcosa del tipofunc 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 il

Probabilmente cambierei il nome del metodo in countableRangee ritornereiCountableRange
— Leo Dabus il

Buoni suggerimenti @LeoDabus su tutta la linea. Ho aggiunto tutti gli argomenti dell'intervallo (di ...), quindi ora puoi chiamare countableRange (di :, opzioni :, intervallo:, locale :). Ho aggiornato il Gist, aggiornerò anche questo post.
— Zack

Non credo che l'intervallo sia necessario considerando che puoi chiamare quel metodo su una sottostringa
— Leo Dabus,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.