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.