Qual è il modo più succinto per rimuovere il primo carattere da una stringa in Swift?


122

Voglio eliminare il primo carattere da una stringa. Finora, la cosa più succinta che ho escogitato è:

display.text = display.text!.substringFromIndex(advance(display.text!.startIndex, 1))

So che non possiamo indicizzare in una stringa con un a Intcausa di Unicode, ma questa soluzione sembra terribilmente prolissa. C'è un altro modo in cui sto trascurando?


3
In realtà puoi evitare il tutto eseguendo il advancecasting display.text!su NSString. Non sto dicendo che sia una buona soluzione, sto solo correggendo un possibile malinteso. Con NSString, puoi indicizzarlo con Int. - E il motivo per cui non puoi indicizzare con Int non è a causa di Unicode; è perché un carattere può essere costituito da più punti di codice composti.
matt

Se lo stai facendo per capitalizzare, StringSwift 3 ha introdotto la capitalizedfunzione String.
Vince O'Sullivan

Risposte:


208

Se stai usando Swift 3 , puoi ignorare la seconda sezione di questa risposta. La buona notizia è che ora è di nuovo succinto! Sto solo usando il nuovo metodo remove (at :) di String.

var myString = "Hello, World"
myString.remove(at: myString.startIndex)

myString // "ello, World"

Mi piace la dropFirst()funzione globale per questo.

let original = "Hello" // Hello
let sliced = dropFirst(original) // ello

È breve, chiaro e funziona per tutto ciò che è conforme al protocollo Sliceable.

Se stai usando Swift 2 , questa risposta è cambiata. È comunque possibile utilizzare dropFirst, ma non senza eliminare il primo carattere dalla charactersproprietà delle stringhe e quindi riconvertire il risultato in una stringa. dropFirst è diventato anche un metodo, non una funzione.

let original = "Hello" // Hello
let sliced = String(original.characters.dropFirst()) // ello

Un'altra alternativa è usare la funzione suffisso per unire la stringa UTF16View. Naturalmente, anche questo deve essere riconvertito in una stringa in seguito.

let original = "Hello" // Hello
let sliced = String(suffix(original.utf16, original.utf16.count - 1)) // ello

Tutto questo per dire che la soluzione che ho fornito originariamente si è rivelata non il modo più succinto per farlo nelle versioni più recenti di Swift. Consiglio di ricorrere alla soluzione di @chris removeAtIndex()se stai cercando una soluzione breve e intuitiva.

var original = "Hello" // Hello
let removedChar = original.removeAtIndex(original.startIndex)

original // ello

E come sottolineato da @vacawama nei commenti qui sotto, un'altra opzione che non modifica la stringa originale consiste nell'usare substringFromIndex.

let original = "Hello" // Hello
let substring = original.substringFromIndex(advance(original.startIndex, 1)) // ello

Oppure, se stai cercando di eliminare un carattere all'inizio e alla fine della stringa, puoi usare substringWithRange. Assicurati solo di proteggerti dalla condizione quando startIndex + n > endIndex - m.

let original = "Hello" // Hello

let newStartIndex = advance(original.startIndex, 1)
let newEndIndex = advance(original.endIndex, -1)

let substring = original.substringWithRange(newStartIndex..<newEndIndex) // ell

L'ultima riga può anche essere scritta usando la notazione in pedice.

let substring = original[newStartIndex..<newEndIndex]

2
E non richiede che tu entri nel mondo della Fondazione.
matt

2
Ed è molto veloce / fp.
David Berry

Grazie, è molto più elegante. Programma in Objective C da anni e sto seguendo il corso di programmazione iOS iTunes U Stanford in Swift. Ho ancora molto da imparare sul nuovo paradigma.
SSteve

1
Tieni presente che durante l'utilizzo di dropLast o dropFirst, scartare la stringa altrimenti non funzionerà.
Bhavuk Jain

3
Bella risposta! Penso che valga la pena notare il valore di ritorno del remove(at:)metodo: è il carattere rimosso, non la nuova stringa. Ad esempio let removedCharacter = myString.remove(at: myString.startIndex),. Stavo commettendo l'errore di restituire il risultato della chiamata al metodo, dimenticando che questo era il caso in questo approccio. Buona programmazione a tutti!
kbpontius

119

Aggiornamento per Swift 4

In Swift 4, Stringè di Collectionnuovo conforme a , quindi è possibile utilizzare dropFirste dropLasttagliare l'inizio e la fine delle stringhe. Il risultato è di tipo Substring, quindi devi passarlo al Stringcostruttore per ottenere un String:

let str = "hello"
let result1 = String(str.dropFirst())    // "ello"
let result2 = String(str.dropLast())     // "hell"

dropFirst()e dropLast()prendi anche un Intper specificare il numero di caratteri da eliminare:

let result3 = String(str.dropLast(3))    // "he"
let result4 = String(str.dropFirst(4))   // "o"

Se specifichi più caratteri da eliminare rispetto a quelli presenti nella stringa, il risultato sarà la stringa vuota ( "").

let result5 = String(str.dropFirst(10))  // ""

Aggiornamento per Swift 3

Se vuoi solo rimuovere il primo carattere e vuoi cambiare la stringa originale in posizione, vedi la risposta di @ MickMacCallum. Se vuoi creare una nuova stringa nel processo, usa substring(from:). Con un'estensione aString , puoi nascondere la bruttezza substring(from:)e substring(to:)creare utili aggiunte per tagliare l'inizio e la fine di un String:

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return substring(from: index(startIndex, offsetBy: count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
        return substring(to: index(endIndex, offsetBy: -count))
    }
}

"hello".chopPrefix()    // "ello"
"hello".chopPrefix(3)   // "lo"

"hello".chopSuffix()    // "hell"
"hello".chopSuffix(3)   // "he"

Come dropFirste dropLastprima di loro, queste funzioni andranno in crash se non ci sono abbastanza lettere disponibili nella stringa. Spetta al chiamante usarli correttamente. Questa è una decisione progettuale valida. Si potrebbe scriverli per restituire un optional che poi dovrebbe essere scartato dal chiamante.


Swift 2.x

Ahimè a Swift 2 , dropFirste dropLast(la migliore soluzione precedente) non sono così conveniente come prima. Con un'estensione a String, puoi nascondere la bruttezza disubstringFromIndex e substringToIndex:

extension String {
    func chopPrefix(count: Int = 1) -> String {
         return self.substringFromIndex(advance(self.startIndex, count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringToIndex(advance(self.endIndex, -count))
    }
}

"hello".chopPrefix()    // "ello"
"hello".chopPrefix(3)   // "lo"

"hello".chopSuffix()    // "hell"
"hello".chopSuffix(3)   // "he"

Piace dropFirste dropLastprima di loro, queste funzioni andranno in crash se non ci sono abbastanza lettere disponibili nella stringa. Spetta al chiamante usarli correttamente. Questa è una decisione progettuale valida. Si potrebbe scriverli per restituire un optional che poi dovrebbe essere scartato dal chiamante.


In Swift 1.2 , dovrai chiamarechopPrefix questo modo:

"hello".chopPrefix(count: 3)  // "lo"

oppure puoi aggiungere un trattino basso _alle definizioni di funzione per sopprimere il nome del parametro:

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
         return self.substringFromIndex(advance(self.startIndex, count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
        return self.substringToIndex(advance(self.endIndex, -count))
    }
}

3
Sono ancora sbalordito da quanto sia complicata la manipolazione delle corde incorporata di Swift. Voglio dire che questa è roba di base, di base; non dovrebbe sembrare che sto implementando un parser. Grazie per l'estensione per semplificare questo. È sconcertante che Apple non aggiunga semplicemente questo alla classe String! Forse è ancora molto mutevole.
devios1

Questo è ciò che chiamano bambini "evoluzione". Sarebbe divertente se non dovessimo affrontarlo ogni giorno. So che ci sono ragioni per cui l'API String è così, ma, davvero, questo deve rimandare così tante potenziali conversioni a questo linguaggio. Il commentatore precedente ha assolutamente ragione: dovrebbe essere roba di base.
Quintin Willison

17

Swift 2.2

"advance" non è disponibile: chiama il metodo "advancedBy (n)" sull'indice

    func chopPrefix(count: Int = 1) -> String {
        return self.substringFromIndex(self.startIndex.advancedBy(count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringFromIndex(self.endIndex.advancedBy(count))
    }

Swift 3.0

    func chopPrefix(_ count: Int = 1) -> String {
        return self.substring(from: self.characters.index(self.startIndex, offsetBy: count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
       return self.substring(to: self.characters.index(self.endIndex, offsetBy: -count))
    }

Swift 3.2

Una visualizzazione del contenuto della stringa come raccolta di caratteri.

@available(swift, deprecated: 3.2, message: "Please use String or Substring directly")
public var characters: String.CharacterView
func chopPrefix(_ count: Int = 1) -> String {
    if count >= 0 && count <= self.count {
        return self.substring(from: String.Index(encodedOffset: count))
    }
    return ""
}

func chopSuffix(_ count: Int = 1) -> String {
    if count >= 0 && count <= self.count {
        return self.substring(to: String.Index(encodedOffset: self.count - count))
    }
    return ""
}

Swift 4

extension String {

    func chopPrefix(_ count: Int = 1) -> String {
        if count >= 0 && count <= self.count {
            let indexStartOfText = self.index(self.startIndex, offsetBy: count)
            return String(self[indexStartOfText...])
        }
        return ""
    }

    func chopSuffix(_ count: Int = 1) -> String {
        if count >= 0 && count <= self.count {
            let indexEndOfText = self.index(self.endIndex, offsetBy: -count)
            return String(self[..<indexEndOfText])
        }
        return ""
    }
}

1
Penso che tu abbia dimenticato di aggiungere il conteggio delle negazioni alla funzione chopSuffix
Andy


10

Dipende da quello che vuoi che sia il risultato finale (mutante vs non mutevole).

A partire da Swift 4.1:

mutando:

var str = "hello"
str.removeFirst() // changes str 

Nonmutating:

let str = "hello"
let strSlice = str.dropFirst() // makes a slice without the first letter
let str2 = String(strSlice)

Appunti:

  • Ho inserito un ulteriore passaggio nonmutatingnell'esempio per chiarezza. Soggettivamente, combinare gli ultimi due passaggi sarebbe più succinto.
  • La denominazione di mi dropFirstsembra un po 'strana perché se sto comprendendo correttamente le linee guida per la progettazione dell'API Swift , dropFirstdovrebbe davvero essere qualcosa di simile dropingFirstperché non muta. Solo un pensiero :).

1
anzi, dovrebbe essere droppingFirst- hanno incasinato!
Fattie

6

Che dire di questo?

s.removeAtIndex(s.startIndex)

Questo ovviamente presuppone che la tua stringa sia mutabile. Restituisce il carattere che è stato rimosso, ma altera la stringa originale.


6

Le risposte precedenti sono abbastanza buone, ma ad oggi, penso che questo possa essere il modo più succinto per rimuovere il primo carattere da una stringa in Swift 4 :

var line: String = "This is a string..."
var char: Character? = nil

char = line.removeFirst()

print("char = \(char)")  // char = T
print("line = \(line)")  // line = his is a string ...

1

Non conosco niente di più succinto fuori dagli schemi, ma potresti facilmente implementare il prefisso ++, ad es.

public prefix func ++ <I: ForwardIndexType>(index: I) -> I {
    return advance(index, 1)
}

Dopodiché puoi usarlo a tuo piacimento in modo molto succinto:

str.substringFromIndex(++str.startIndex)

1

In Swift 2 usa questa estensione String:

extension String
{
    func substringFromIndex(index: Int) -> String
    {
        if (index < 0 || index > self.characters.count)
        {
            print("index \(index) out of bounds")
            return ""
        }
        return self.substringFromIndex(self.startIndex.advancedBy(index))
    }
}

display.text = display.text!.substringFromIndex(1)

1

"en_US, fr_CA, es_US" .chopSuffix (5) .chopPrefix (5) // ", fr_CA,"

extension String {
    func chopPrefix(count: Int = 1) -> String {
        return self.substringFromIndex(self.startIndex.advancedBy(count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringToIndex(self.endIndex.advancedBy(-count))
    }
}

0

Per rimuovere il primo carattere dalla stringa

let choppedString = String(txtField.text!.characters.dropFirst())

@JasonFoglia non so se dai il voto o no. se dai voto, puoi spiegarlo in dettaglio.
Hardik Thakkar

Ho ricontrollato e ho scoperto che è corretto. Ho modificato la risposta in modo da poter votare correttamente su questo.
Jason Foglia

0

Ecco una versione dell'estensione per il crash save di Swift4chopPrefix , che lascia chopSuffixalla community ...

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return count>self.count ? self : String(self[index(self.startIndex, offsetBy: count)...])
    }
 }

0

Swift3

extension String {
    func chopPrefix(_ count: UInt = 1) -> String {
        return substring(from: characters.index(startIndex, offsetBy: Int(count)))
    }

    func chopSuffix(_ count: UInt = 1) -> String {
        return substring(to: characters.index(endIndex, offsetBy: -Int(count)))
    }
}

class StringChopTests: XCTestCase {
    func testPrefix() {
        XCTAssertEqual("original".chopPrefix(0), "original")
        XCTAssertEqual("Xfile".chopPrefix(), "file")
        XCTAssertEqual("filename.jpg".chopPrefix(4), "name.jpg")
    }

    func testSuffix() {
        XCTAssertEqual("original".chopSuffix(0), "original")
        XCTAssertEqual("fileX".chopSuffix(), "file")
        XCTAssertEqual("filename.jpg".chopSuffix(4), "filename")
    }
}

Penso che dovresti aggiungere un test in cui l'ingresso è in un valore negativo
Moustafa Baalbaki
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.