Was ist der prägnanteste Weg, um das erste Zeichen aus einer Zeichenfolge in Swift zu entfernen?

122

Ich möchte das erste Zeichen aus einer Zeichenfolge löschen. Das Prägnanteste, was ich mir bisher ausgedacht habe, ist:

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

Ich weiß, dass wir Intwegen Unicode nicht in einen String mit einem indizieren können , aber diese Lösung scheint furchtbar ausführlich zu sein. Gibt es einen anderen Weg, den ich übersehen habe?

SSteve
quelle
3
Eigentlich können Sie das Ganze vermeiden advance, indem Sie display.text!auf NSString übertragen. Ich sage nicht, dass dies eine gute Lösung ist - nur um ein mögliches Missverständnis zu korrigieren. Mit NSString können Sie mit Int. - Und der Grund, warum Sie nicht mit Int indizieren können, liegt nicht an Unicode. Dies liegt daran, dass ein Zeichen aus mehreren zusammengesetzten Codepunkten bestehen kann.
Matt
Wenn Sie dies tun, um Kapital zu Stringschlagen, hat Swift 3 die capitalizedFunktion eingeführt String.
Vince O'Sullivan

Antworten:

207

Wenn Sie Swift 3 verwenden , können Sie den zweiten Abschnitt dieser Antwort ignorieren. Die gute Nachricht ist, dass dies jetzt wieder prägnant ist! Verwenden Sie einfach die neue Methode remove (at :) von String.

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

myString // "ello, World"

Ich mag die globale dropFirst()Funktion dafür.

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

Es ist kurz, klar und funktioniert für alles, was dem Sliceable-Protokoll entspricht.

Wenn Sie Swift 2 verwenden , hat sich diese Antwort geändert. Sie können dropFirst weiterhin verwenden, jedoch nicht, ohne das erste Zeichen aus Ihrer Strings- charactersEigenschaft zu löschen und das Ergebnis dann wieder in einen String zu konvertieren. dropFirst ist auch eine Methode geworden, keine Funktion.

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

Eine andere Alternative besteht darin, die Suffixfunktion zum Spleißen der Zeichenfolgen zu verwenden UTF16View. Natürlich muss dies auch danach wieder in einen String umgewandelt werden.

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

All dies bedeutet, dass sich herausgestellt hat, dass die ursprünglich von mir bereitgestellte Lösung in neueren Versionen von Swift nicht die prägnanteste Methode ist. Ich empfehle, auf die Lösung von @chris zurückzugreifen, removeAtIndex()wenn Sie nach einer kurzen und intuitiven Lösung suchen.

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

original // ello

Und wie von @vacawama in den Kommentaren unten hervorgehoben, besteht eine andere Option, die den ursprünglichen String nicht ändert, darin, substringFromIndex zu verwenden.

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

Oder wenn Sie zufällig versuchen, ein Zeichen am Anfang und Ende des Strings abzulegen, können Sie substringWithRange verwenden. Achten Sie nur darauf, sich vor dem Zustand zu schützen, wenn 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

Die letzte Zeile kann auch in Indexnotation geschrieben werden.

let substring = original[newStartIndex..<newEndIndex]
Mick MacCallum
quelle
2
Und Sie müssen nicht in die Foundation-Welt eintreten.
Matt
2
Und es ist sehr schnell / fp.
David Berry
Danke, das ist viel eleganter. Ich programmiere seit Jahren in Objective C und nehme am iTunes U Stanford iOS-Programmierkurs in Swift teil. Ich muss noch viel über das neue Paradigma lernen.
SSteve
1
Denken Sie bei der Verwendung von dropLast oder dropFirst daran, die Zeichenfolge abzuwickeln, da sie sonst nicht funktioniert.
Bhavuk Jain
3
Gute Antwort! Ich denke, es lohnt sich, den Rückgabewert der remove(at:)Methode zu beachten : Es ist das entfernte Zeichen, nicht die neue Zeichenfolge. Zum Beispiel let removedCharacter = myString.remove(at: myString.startIndex). Ich habe den Fehler gemacht, das Ergebnis des Methodenaufrufs zurückzugeben, und dabei vergessen, dass dies bei diesem Ansatz der Fall war. Viel Spaß beim Codieren!
kbpontius
118

Update für Swift 4

In Swift 4 Stringpasst sich Collectionwieder an, so dass es möglich ist, die Anfänge und Enden von Strings zu verwenden dropFirstund dropLastzu kürzen. Das Ergebnis ist vom Typ Substring, daher müssen Sie es an den StringKonstruktor übergeben, um Folgendes zurückzugewinnen String:

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

dropFirst()und dropLast()nehmen Sie auch ein Int, um die Anzahl der zu löschenden Zeichen anzugeben:

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

Wenn Sie mehr Zeichen angeben, die gelöscht werden sollen, als in der Zeichenfolge enthalten sind, ist das Ergebnis die leere Zeichenfolge ( "").

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

Update für Swift 3

Wenn Sie nur das erste Zeichen entfernen und die ursprüngliche Zeichenfolge ändern möchten, lesen Sie die Antwort von @ MickMacCallum. Wenn Sie dabei eine neue Zeichenfolge erstellen möchten, verwenden Sie substring(from:). Mit der Erweiterung auf Stringkönnen Sie die Hässlichkeit von verbergen substring(from:)und substring(to:)nützliche Ergänzungen erstellen, um den Anfang und das Ende von 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"

Wie dropFirstund dropLastvor ihnen stürzen diese Funktionen ab, wenn im String nicht genügend Buchstaben verfügbar sind. Es liegt in der Verantwortung des Anrufers, sie ordnungsgemäß zu verwenden. Dies ist eine gültige Entwurfsentscheidung. Man könnte sie schreiben, um ein optionales zurückzugeben, das dann vom Anrufer ausgepackt werden müsste.


Swift 2.x.

Ach in Swift 2 , dropFirstund dropLast(die bisher besten Lösung) ist nicht so bequem wie sie vorher waren. Mit einer Erweiterung auf Stringkönnen Sie die Hässlichkeit von substringFromIndexund verbergen 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"

Wie dropFirstund dropLastvor ihnen stürzen diese Funktionen ab, wenn im String nicht genügend Buchstaben verfügbar sind. Es liegt in der Verantwortung des Anrufers, sie ordnungsgemäß zu verwenden. Dies ist eine gültige Entwurfsentscheidung. Man könnte sie schreiben, um ein optionales zurückzugeben, das dann vom Anrufer ausgepackt werden müsste.


In Swift 1.2 müssen Sie wie folgt aufrufen chopPrefix:

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

oder Sie können _den Funktionsdefinitionen einen Unterstrich hinzufügen , um den Parameternamen zu unterdrücken:

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))
    }
}
vacawama
quelle
3
Ich bin immer noch überwältigt davon, wie kompliziert Swifts eingebaute String-Manipulation ist. Ich meine, das ist grundlegend, grundlegend; Es sollte nicht so aussehen, als würde ich einen Parser implementieren. Vielen Dank für die Erweiterung, um dies zu vereinfachen. Es ist verwirrend, dass Apple dies nicht einfach der String-Klasse hinzufügen würde! Vielleicht ist es immer noch im Fluss.
Devios1
Dies ist, was sie "Evolution" Kinder nennen. Es wäre lustig, wenn wir uns nicht jeden Tag damit befassen müssten. Ich weiß, dass es Gründe dafür gibt, dass die String-API so ist, wie sie ist, aber das muss wirklich so viele potenzielle Konvertierungen in diese Sprache verhindern. Der vorherige Kommentator hat absolut Recht - dies sollte grundlegend sein.
Quintin Willison
17

Swift 2.2

'advanced' ist nicht verfügbar: Rufen Sie die Methode 'advancedBy (n)' für den Index auf

    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

Eine Ansicht des Inhalts der Zeichenfolge als Sammlung von Zeichen.

@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 ""
    }
}
Mohamed Jaleel Nazir
quelle
1
Ich denke, Sie haben vergessen, die Negationszahl zur chopSuffix-Funktion hinzuzufügen
Andy
10

Hängt davon ab, wie das Endergebnis aussehen soll (mutieren oder nicht mutieren).

Ab Swift 4.1:

Mutieren:

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

Nicht mutierend:

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

Anmerkungen:

  • Der nonmutatingKlarheit halber habe ich dem Beispiel einen zusätzlichen Schritt hinzugefügt. Subjektiv wäre die Kombination der letzten beiden Schritte prägnanter.
  • Die Benennung von dropFirstscheint mir etwas seltsam, denn wenn ich die Swift API Design Guidelines richtig verstehe , dropFirstsollte es wirklich so sein, dropingFirstweil es nicht mutiert. Nur ein Gedanke :).
Jason z
quelle
1
in der Tat sollte es sein droppingFirst- sie haben es vermasselt!
Fattie
6

Was ist damit?

s.removeAtIndex(s.startIndex)

Dies setzt natürlich voraus, dass Ihre Zeichenfolge veränderbar ist. Es gibt das Zeichen zurück, das entfernt wurde, ändert jedoch die ursprüngliche Zeichenfolge.

chris
quelle
6

Die vorherigen Antworten sind ziemlich gut, aber ab heute denke ich, dass dies der prägnanteste Weg ist, um das erste Zeichen aus einer Zeichenfolge in Swift 4 zu entfernen :

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 ...
ByteSlinger
quelle
1

Ich weiß nichts prägnanteres von der Stange, aber Sie könnten leicht ein Präfix implementieren ++, z.

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

Danach können Sie es nach Herzenslust sehr prägnant verwenden:

str.substringFromIndex(++str.startIndex)
Gregory Higley
quelle
1

Verwenden Sie in Swift 2 diese String-Erweiterung:

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)
ChikabuZ
quelle
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))
    }
}
Andy
quelle
0

So entfernen Sie das erste Zeichen aus der Zeichenfolge

let choppedString = String(txtField.text!.characters.dropFirst())
Hardik Thakkar
quelle
@ JasonFoglia Ich weiß nicht, ob du abstimmst oder nicht. Wenn Sie Ihre Stimme abgeben, können Sie dies bitte ausführlich erläutern.
Hardik Thakkar
Ich habe dies erneut überprüft und festgestellt, dass es korrekt ist. Ich habe die Antwort bearbeitet, damit ich richtig darüber abstimmen kann.
Jason Foglia
0

Hier ist eine Swift4- Version der chopPrefixErweiterung chopSuffixzum Absturz , die der Community überlassen bleibt ...

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return count>self.count ? self : String(self[index(self.startIndex, offsetBy: count)...])
    }
 }
iDoc
quelle
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")
    }
}
neoneye
quelle
Ich denke, Sie sollten einen Test hinzufügen, bei dem die Eingabe einen negativen Wert hat
Moustafa Baalbaki