Quelle est la manière la plus succincte de supprimer le premier caractère d'une chaîne dans Swift?


122

Je souhaite supprimer le premier caractère d'une chaîne. Jusqu'à présent, la chose la plus succincte que j'ai trouvée est:

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

Je sais que nous ne pouvons pas indexer dans une chaîne avec un à Intcause d'Unicode, mais cette solution semble terriblement verbeuse. Y a-t-il une autre façon que je néglige?


3
En fait, vous pouvez éviter tout advancecela en display.text!effectuant un casting vers NSString. Je ne dis pas que c'est une bonne solution - il suffit de corriger une éventuelle idée fausse. Avec NSString, vous pouvez y indexer avec Int. - Et la raison pour laquelle vous ne pouvez pas indexer avec Int n'est pas à cause d'Unicode; c'est parce qu'un caractère peut être composé de plusieurs points de code composés.
mat

Si vous faites cela afin de capitaliser le Stringalors Swift 3 a introduit la capitalizedfonction à String.
Vince O'Sullivan

Réponses:


208

Si vous utilisez Swift 3 , vous pouvez ignorer la deuxième section de cette réponse. La bonne nouvelle est que c'est à nouveau succinct! Simplement en utilisant la nouvelle méthode remove (at :) de String.

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

myString // "ello, World"

J'aime la dropFirst()fonction globale pour cela.

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

C'est court, clair et fonctionne pour tout ce qui est conforme au protocole Sliceable.

Si vous utilisez Swift 2 , cette réponse a changé. Vous pouvez toujours utiliser dropFirst, mais pas sans supprimer le premier caractère de votre characterspropriété strings , puis reconvertir le résultat en String. dropFirst est également devenu une méthode, pas une fonction.

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

Une autre alternative consiste à utiliser la fonction suffixe pour épisser la chaîne UTF16View. Bien sûr, cela doit également être reconverti en chaîne par la suite.

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

Tout cela pour dire que la solution que j'ai fournie à l'origine ne s'est pas avérée être le moyen le plus succinct de le faire dans les nouvelles versions de Swift. Je recommande de revenir sur la solution de @chris removeAtIndex()si vous recherchez une solution courte et intuitive.

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

original // ello

Et comme indiqué par @vacawama dans les commentaires ci-dessous, une autre option qui ne modifie pas la chaîne d'origine est d'utiliser substringFromIndex.

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

Ou si vous cherchez à supprimer un caractère du début et de la fin de la chaîne, vous pouvez utiliser substringWithRange. Assurez-vous simplement de vous prémunir contre la condition quand 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

La dernière ligne peut également être écrite en utilisant la notation en indice.

let substring = original[newStartIndex..<newEndIndex]

2
Et cela ne vous oblige pas à entrer dans le monde de la Fondation.
mat

2
Et c'est très rapide / fp.
David Berry

Merci, c'est beaucoup plus élégant. Je programme en Objective C depuis des années et je suis le cours de programmation iTunes U Stanford iOS en Swift. J'ai encore beaucoup à apprendre sur le nouveau paradigme.
SSteve

1
Gardez à l'esprit que lorsque vous utilisez dropLast ou dropFirst, dépliez la chaîne sinon cela ne fonctionnera pas.
Bhavuk Jain

3
Très bonne réponse! Je pense qu'il vaut la peine de noter la valeur de retour de la remove(at:)méthode: c'est le caractère supprimé, pas la nouvelle chaîne. Par exemple let removedCharacter = myString.remove(at: myString.startIndex),. Je faisais l'erreur de renvoyer le résultat de l'appel de méthode, oubliant que c'était le cas dans cette approche. Bon codage à tous!
kbpontius le

119

Mise à jour pour Swift 4

Dans Swift 4, se Stringconforme à Collectionnouveau, il est donc possible d'utiliser dropFirstet dropLastde couper les débuts et les fins des chaînes. Le résultat est de type Substring, vous devez donc le transmettre au Stringconstructeur pour récupérer un String:

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

dropFirst()et dropLast()prenez également un Intpour spécifier le nombre de caractères à supprimer:

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

Si vous spécifiez plus de caractères à supprimer que dans la chaîne, le résultat sera la chaîne vide ( "").

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

Mise à jour pour Swift 3

Si vous souhaitez simplement supprimer le premier caractère et que vous souhaitez modifier la chaîne d'origine en place, consultez la réponse de @ MickMacCallum. Si vous souhaitez créer une nouvelle chaîne dans le processus, utilisez substring(from:). Avec une extension à String, vous pouvez masquer la laideur de substring(from:)et substring(to:)créer des ajouts utiles pour couper le début et la fin d'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"

Comme dropFirstet dropLastavant eux, ces fonctions planteront s'il n'y a pas assez de lettres disponibles dans la chaîne. Il incombe à l'appelant de les utiliser correctement. Il s'agit d'une décision de conception valide. On pourrait les écrire pour renvoyer un optionnel qui devrait alors être déballé par l'appelant.


Swift 2.x

Hélas dans Swift 2 , dropFirstet dropLast(la meilleure solution précédente) ne sont pas aussi pratiques qu'avant. Avec une extension de String, vous pouvez masquer la laideur de substringFromIndexet 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"

Comme dropFirstet dropLastavant eux, ces fonctions planteront s'il n'y a pas assez de lettres disponibles dans la chaîne. Il incombe à l'appelant de les utiliser correctement. Il s'agit d'une décision de conception valide. On pourrait les écrire pour renvoyer un optionnel qui devrait alors être déballé par l'appelant.


Dans Swift 1.2 , vous devrez appeler chopPrefixcomme ceci:

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

ou vous pouvez ajouter un trait _de soulignement aux définitions de fonction pour supprimer le nom du paramètre:

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
Je suis toujours époustouflé par la complexité de la manipulation des cordes intégrée à Swift. Je veux dire que ce sont des trucs basiques, basiques; cela ne devrait pas avoir l'air d'implémenter un analyseur. Merci pour l'extension pour simplifier cela. C'est déroutant qu'Apple n'ajoute pas simplement cela à la classe String! Peut-être que cela change encore beaucoup.
devios1

C'est ce qu'ils appellent les enfants «d'évolution». Ce serait hilarant si nous n'avions pas à nous en occuper tous les jours. Je sais qu'il y a des raisons pour lesquelles l'API String est telle qu'elle est mais, vraiment, cela doit décourager tant de convertis potentiels à ce langage. Le commentateur précédent a tout à fait raison - cela devrait être un élément de base.
Quintin Willison

17

Swift 2.2

'avance' n'est pas disponible: appelez la méthode 'advancedBy (n)' sur l'index

    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

Une vue du contenu de la chaîne sous la forme d'une collection de caractères.

@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
Je pense que vous avez oublié d'ajouter la négation -count à la fonction chopSuffix
Andy


10

Cela dépend de ce que vous voulez que le résultat final soit (mutant vs non mutant).

À partir de Swift 4.1:

Mutation:

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

Non mutant:

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

Remarques:

  • J'ai mis une étape supplémentaire dans l' nonmutatingexemple pour plus de clarté. Subjectivement, combiner les deux dernières étapes serait plus succinct.
  • Le nom de dropFirstme semble un peu étrange, car si je comprends correctement les directives de conception de l'API Swift , cela dropFirstdevrait vraiment être quelque chose comme dropingFirstparce qu'il est non mutant. Juste une pensée :).

1
en effet, ça devrait être droppingFirst- ils ont foiré!
Fattie le

6

Et ça?

s.removeAtIndex(s.startIndex)

Cela suppose bien sûr que votre chaîne est modifiable. Il renvoie le caractère qui a été supprimé, mais modifie la chaîne d'origine.


6

Les réponses précédentes sont plutôt bonnes, mais à partir d'aujourd'hui, je pense que c'est peut-être le moyen le plus succinct de supprimer le premier caractère d'une chaîne dans 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

Je ne connais rien de plus succinct hors de la boîte, mais vous pouvez facilement implémenter un préfixe ++, par exemple,

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

Après quoi, vous pouvez l'utiliser à votre guise de manière très succincte:

str.substringFromIndex(++str.startIndex)

1

Dans Swift 2, utilisez cette extension 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

Pour supprimer le premier caractère de la chaîne

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

@JasonFoglia je ne sais pas si vous donnez votre vote ou non. si vous donnez votre vote, pouvez-vous expliquer en détail.
Hardik Thakkar

J'ai revérifié cela et j'ai trouvé que c'était correct. J'ai modifié la réponse afin de pouvoir voter correctement à ce sujet.
Jason Foglia

0

Voici une version de sauvegarde du crash Swift4 de l' chopPrefixextension, laissant chopSuffixà la communauté ...

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

Je pense que vous devriez ajouter un test où l'entrée est dans une valeur négative
Moustafa Baalbaki
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.