Holen Sie sich das n-te Zeichen einer Zeichenfolge in der Programmiersprache Swift

419

Wie kann ich das n-te Zeichen einer Zeichenfolge erhalten? Ich habe versucht, Bracket ( []) Accessor ohne Glück.

var string = "Hello, world!"

var firstChar = string[0] // Throws error

FEHLER: 'tiefgestellt' ist nicht verfügbar: Zeichenkette kann nicht mit einem Int abonniert werden. Weitere Informationen finden Sie im Dokumentationskommentar

Mohsen
quelle
1
Die Fehlermeldung "String kann nicht mit einem Int abonniert werden, siehe Dokumentationskommentar zur Diskussion" scheint sich auf github.com/apple/swift/blob/master/stdlib/public/core/…
andrewdotn
Verwenden Sie var firstChar = string.index(string.startIndex, offsetBy: 0)stattdessen
Sazzad Hissain Khan
@SazzadHissainKhan Dies würde zu einem String-Index führen, nicht zu einem Zeichen. Übrigens warum nicht einfach string.startIndex? Für das erste Zeichen string[string.startIndex] oder einfach string.first. Beachten Sie, dass der erste Ansatz, den Sie benötigen würden, um zu überprüfen, ob die Zeichenfolge zuerst leer ist, der zweite ein optionales zurückgibt
Leo Dabus

Antworten:

567

Achtung: Bitte beachten Sie Leo Dabus' Antwort für eine ordnungsgemäße Durchführung für Swift 4 und Swift 5.

Swift 4 oder höher

Der SubstringTyp wurde in Swift 4 eingeführt, um Teilzeichenfolgen schneller und effizienter zu machen, indem der Speicher mit der ursprünglichen Zeichenfolge geteilt wird. Daher sollten die Indexfunktionen dies zurückgeben.

Probieren Sie es hier aus

extension StringProtocol {
    subscript(offset: Int) -> Character { self[index(startIndex, offsetBy: offset)] }
    subscript(range: Range<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence { self[index(startIndex, offsetBy: range.lowerBound)...] }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence { self[...index(startIndex, offsetBy: range.upperBound)] }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence { self[..<index(startIndex, offsetBy: range.upperBound)] }
}

Um das Substringin ein zu konvertieren String, können Sie dies einfach tun String(string[0..2]), aber Sie sollten dies nur tun, wenn Sie vorhaben, die Teilzeichenfolge beizubehalten. Ansonsten ist es effizienter, es zu behalten Substring.

Es wäre großartig, wenn jemand einen guten Weg finden könnte, diese beiden Erweiterungen zu einer zusammenzuführen. Ich habe versucht, StringProtocol ohne Erfolg zu erweitern, da die indexMethode dort nicht existiert. Hinweis: Diese Antwort wurde bereits bearbeitet, ist ordnungsgemäß implementiert und funktioniert jetzt auch für Teilzeichenfolgen. Stellen Sie einfach sicher, dass Sie einen gültigen Bereich verwenden, um Abstürze beim Abonnieren Ihres StringProtocol-Typs zu vermeiden. Für die Subskription mit einem Bereich, der nicht mit Werten außerhalb des Bereichs abstürzt, können Sie diese Implementierung verwenden


Warum ist das nicht eingebaut?

Die Fehlermeldung lautet "Siehe den Dokumentationskommentar zur Diskussion" . Apple bietet die folgende Erklärung in der Datei UnavailableStringAPIs.swift :

Das Abonnieren von Zeichenfolgen mit Ganzzahlen ist nicht verfügbar.

Das Konzept des " ith-Zeichens in einer Zeichenfolge" hat unterschiedliche Interpretationen in verschiedenen Bibliotheken und Systemkomponenten. Die richtige Interpretation sollte entsprechend dem Anwendungsfall und den beteiligten APIs ausgewählt werden, daher String kann keine Ganzzahl verwendet werden.

Swift bietet verschiedene Möglichkeiten, auf die in Zeichenfolgen gespeicherten Zeichendaten zuzugreifen.

  • String.utf8ist eine Sammlung von UTF-8-Codeeinheiten in der Zeichenfolge. Verwenden Sie diese API, wenn Sie die Zeichenfolge in UTF-8 konvertieren. Die meisten POSIX-APIs verarbeiten Zeichenfolgen in UTF-8-Codeeinheiten.

  • String.utf16ist eine Sammlung von UTF-16-Codeeinheiten in Zeichenfolge. Die meisten Cocoa- und Cocoa-Touch-APIs verarbeiten Zeichenfolgen in UTF-16-Codeeinheiten. Zum Beispiel Instanzen von NSRangemit NSAttributedStringund NSRegularExpressionSpeichern von Teilzeichenfolgen-Offsets und -Längen in Form von UTF-16-Codeeinheiten.

  • String.unicodeScalarsist eine Sammlung von Unicode-Skalaren. Verwenden Sie diese API, wenn Sie Zeichendaten auf niedriger Ebene bearbeiten.

  • String.characters ist eine Sammlung erweiterter Graphemcluster, die eine Annäherung an vom Benutzer wahrgenommene Zeichen darstellen.

Beachten Sie, dass bei der Verarbeitung von Zeichenfolgen, die für Menschen lesbaren Text enthalten, die Verarbeitung von Zeichen zu Zeichen so weit wie möglich vermieden werden sollte. Verwenden Sie High-Level - locale-sensitive Unicode Algorithmen statt, zum Beispiel String.localizedStandardCompare(), String.localizedLowercaseString, String.localizedStandardRangeOfString()usw.

Aleclarson
quelle
4
Cannot find an overload for 'advance' that accepts an argument list of type '(String.Index, T)'... String.Indexund Intsind nicht kompatibel.
7
Wenn Sie sehen, Cannot subscript a value of type 'String'...überprüfen Sie diese Antwort: stackoverflow.com/a/31265316/649379
SoftDesigner
24
Wenn ich versuche, dies zu verwenden, bekomme ich Ambiguous use of 'subscript'.
Jowie
18
WARNUNG! Die Erweiterung unten ist schrecklich ineffizient. Jedes Mal, wenn auf eine Zeichenfolge mit einer Ganzzahl zugegriffen wird, wird eine O (n) -Funktion zum Vorrücken des Startindex ausgeführt. Das Ausführen einer linearen Schleife innerhalb einer anderen linearen Schleife bedeutet, dass diese for-Schleife versehentlich O (n2) ist. Wenn die Länge der Zeichenfolge zunimmt, nimmt die Zeit, die diese Schleife benötigt, quadratisch zu. Stattdessen können Sie die Zeichenfolgensammlung der Zeichen verwenden.
Ignaciohugog
2
fatal error: Can't form a Character from an empty String
Devin B
341

Swift 5.2

let str = "abcdef"
str[1 ..< 3] // returns "bc"
str[5] // returns "f"
str[80] // returns ""
str.substring(fromIndex: 3) // returns "def"
str.substring(toIndex: str.length - 2) // returns "abcd"

Sie müssen diese String-Erweiterung zu Ihrem Projekt hinzufügen (es ist vollständig getestet):

extension String {

    var length: Int {
        return count
    }

    subscript (i: Int) -> String {
        return self[i ..< i + 1]
    }

    func substring(fromIndex: Int) -> String {
        return self[min(fromIndex, length) ..< length]
    }

    func substring(toIndex: Int) -> String {
        return self[0 ..< max(0, toIndex)]
    }

    subscript (r: Range<Int>) -> String {
        let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                            upper: min(length, max(0, r.upperBound))))
        let start = index(startIndex, offsetBy: range.lowerBound)
        let end = index(start, offsetBy: range.upperBound - range.lowerBound)
        return String(self[start ..< end])
    }
}

Auch wenn Swift für dieses Problem immer eine sofort einsatzbereite Lösung hatte (ohne die unten angegebene String-Erweiterung), würde ich die Verwendung der Erweiterung nachdrücklich empfehlen . Warum? Weil es mir Dutzende Stunden schmerzhafter Migration von früheren Versionen von Swift erspart hat, bei denen die Syntax von String fast jede Version geändert hat, aber alles, was ich tun musste, war, die Implementierung der Erweiterung zu aktualisieren, anstatt das gesamte Projekt umzugestalten. Treffen Sie Ihre Wahl.

let str = "Hello, world!"
let index = str.index(str.startIndex, offsetBy: 4)
str[index] // returns Character 'o'

let endIndex = str.index(str.endIndex, offsetBy:-2)
str[index ..< endIndex] // returns String "o, worl"

String(str.suffix(from: index)) // returns String "o, world!"
String(str.prefix(upTo: index)) // returns String "Hell"
nalexn
quelle
Wechsel range.upperBound - range.lowerBoundzurange.count
Leo Dabus
Es ist nicht Teil der ursprünglichen Frage, aber ... es wäre schön, wenn diese Aufgabe auch unterstützt würde. ZB s [i] = "a" :).
Chris Prince
2
Ich glaube, mit Swift 4.2 sind Indizes nicht mehr verfügbar. Ich erhalte die Fehlermeldung: 'tiefgestellt' ist nicht verfügbar: Zeichenkette mit einem Int kann nicht tiefgestellt werden.
Weitere Informationen finden
2
@ ChrisPrinceextension StringProtocol where Self: RangeReplaceableCollection { subscript(offset: Int) -> Element { get { return self[index(startIndex, offsetBy: offset)] } set { let start = index(startIndex, offsetBy: offset) replaceSubrange(start..<index(after: start), with: [newValue]) } } }
Leo Dabus
Dies sollten eingebaute Funktionen sein
Ammar Mujeeb
150

Ich habe mir gerade diese nette Problemumgehung ausgedacht

var firstChar = Array(string)[0]
Jens Wirth
quelle
2
Dies ist eine gute schnelle Lösung für den (allgemeinen) Fall, in dem Sie wissen, dass Sie UTF8- oder ASCII-codierte Zeichenfolgen haben. Stellen Sie nur sicher, dass sich die Zeichenfolgen niemals in einer Codierung befinden, die mehr als ein Byte verwendet.
Jeff Hay
47
Das scheint äußerst ineffizient zu sein, da Sie die gesamte Zeichenfolge kopieren, um das erste Zeichen zu erhalten. Verwenden Sie string [string. startIndex] anstelle von 0, wie Sulthan betonte.
Björn
6
String auspacken: var firstChar = Array(string!)[0]Andernfalls heißt esadd arrayLiteral
Zaid Pathan
2
Ich glaube nicht, dass dies sauber ist, tatsächlich geht es darum. Ich bin mir nicht ganz sicher, welcher Initialisierer in Array zuerst verwendet wird, was dazu führt (und ich gehe davon aus, dass es sich um den SequenceType-Initialisierer handelt, der die Zeichen der Zeichenfolge als einzelne Komponenten für das Array sammelt). Dies ist überhaupt nicht explizit und kann in Zukunft ohne Typumwandlung korrigiert werden. Dies funktioniert auch nicht, wenn Sie die Kurzform für das Array über [string] .first verwenden. Die Lösung von @ Sulthan funktioniert am besten, wenn die eingebrannten Indexwerte verwendet werden. Es ist viel klarer, was hier passiert.
TheCodingArt
2
Wow, Compilersegmentfehler!
Frank
124

Keine Indizierung mit ganzen Zahlen, nur mit String.Index. Meist mit linearer Komplexität. Sie können damit auch Bereiche erstellen String.Indexund Teilzeichenfolgen abrufen.

Swift 3.0

let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.index(before: someString.endIndex)]
let charAtIndex = someString[someString.index(someString.startIndex, offsetBy: 10)]

let range = someString.startIndex..<someString.index(someString.startIndex, offsetBy: 10)
let substring = someString[range]

Swift 2.x.

let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.endIndex.predecessor()]
let charAtIndex = someString[someString.startIndex.advanceBy(10)]

let range = someString.startIndex..<someString.startIndex.advanceBy(10)
let subtring = someString[range]

Beachten Sie, dass Sie niemals einen Index (oder Bereich) verwenden können, der von einer Zeichenfolge zu einer anderen Zeichenfolge erstellt wurde

let index10 = someString.startIndex.advanceBy(10)

//will compile
//sometimes it will work but sometimes it will crash or result in undefined behaviour
let charFromAnotherString = anotherString[index10]
Sulthan
quelle
7
StringIndizes sind für eine Zeichenfolge eindeutig. Dies liegt daran, dass unterschiedliche Zeichenfolgen unterschiedliche UTF-16-Einheiten mit mehreren Einheiten Charactersund / oder unterschiedliche Positionen aufweisen können, sodass die UTF-16-Einheitenindizes nicht übereinstimmen und möglicherweise über das Ende oder den Punkt innerhalb einer UTF-16-Einheit mit mehreren Einheiten hinausgehen Character.
Zaph
@ Zaph Das ist offensichtlich.
Sulthan
3
Erklären Sie, warum Sie sagen: "Manchmal stürzt es ab oder führt zu undefiniertem Verhalten." Vielleicht ist es besser zu sagen, tu es einfach nicht, weil ...
zaph
1
@ Sulthan ..ist jetzt ..<(in Ihrer Aufgabe zu range)
Aaron Brager
3
@CajunLuke Ich weiß, es ist schon eine Weile her, seit du diesen Kommentar gepostet hast, aber sieh dir diese Antwort an . Sie können verwendenvar lastChar = string[string.endIndex.predecessor()]
David L
122

Xcode 11 • Swift 5.1

Sie können StringProtocol erweitern, um den Index auch für die Teilzeichenfolgen verfügbar zu machen:

extension StringProtocol {
    subscript(_ offset: Int)                     -> Element     { self[index(startIndex, offsetBy: offset)] }
    subscript(_ range: Range<Int>)               -> SubSequence { prefix(range.lowerBound+range.count).suffix(range.count) }
    subscript(_ range: ClosedRange<Int>)         -> SubSequence { prefix(range.lowerBound+range.count).suffix(range.count) }
    subscript(_ range: PartialRangeThrough<Int>) -> SubSequence { prefix(range.upperBound.advanced(by: 1)) }
    subscript(_ range: PartialRangeUpTo<Int>)    -> SubSequence { prefix(range.upperBound) }
    subscript(_ range: PartialRangeFrom<Int>)    -> SubSequence { suffix(Swift.max(0, count-range.lowerBound)) }
}

extension LosslessStringConvertible {
    var string: String { .init(self) }
}

extension BidirectionalCollection {
    subscript(safe offset: Int) -> Element? {
        guard !isEmpty, let i = index(startIndex, offsetBy: offset, limitedBy: index(before: endIndex)) else { return nil }
        return self[i]
    }
}

Testen

let test = "Hello USA 🇺🇸!!! Hello Brazil 🇧🇷!!!"
test[safe: 10]   // "🇺🇸"
test[11]   // "!"
test[10...]   // "🇺🇸!!! Hello Brazil 🇧🇷!!!"
test[10..<12]   // "🇺🇸!"
test[10...12]   // "🇺🇸!!"
test[...10]   // "Hello USA 🇺🇸"
test[..<10]   // "Hello USA "
test.first   // "H"
test.last    // "!"

// Subscripting the Substring
 test[...][...3]  // "Hell"

// Note that they all return a Substring of the original String.
// To create a new String from a substring
test[10...].string  // "🇺🇸!!! Hello Brazil 🇧🇷!!!"
Leo Dabus
quelle
Darf ich fragen, was "self [index (startIndex, offsetBy: i)]" ist? Und wie funktioniert "self [i]"?
Allenlinli
1
Hallo Leo, danke für die Lösung! Ich habe gerade (heute) von Swift 2.3 auf 3 gewechselt und Ihr Lösungsindex (Bereich: Bereich <Int>) gibt den Fehler "Zusätzliches Argument 'LimitedBy' im Aufruf" aus. Was denkst du kann falsch sein?
Ahmet Akkök
@ AhmetAkkök Bist du sicher, dass du den Code nicht geändert hast?
Leo Dabus
1
@Leo es stellte sich heraus, dass ich nicht das gesamte Projekt konvertiert habe, aber in der App nicht die Erweiterung. Ich habe den Vorgang sowohl für die App als auch für die Erweiterung wiederholt und es funktioniert jetzt in Ordnung. Ihre Hilfe wird sehr geschätzt!
Ahmet Akkök
Das ist sehr komplizierter Code. Was ist der Vorteil gegenüber return String(Array(characters)[range])Swift 3?
Dan Rosenstark
67

Swift 4

let str = "My String"

Zeichenfolge am Index

let index = str.index(str.startIndex, offsetBy: 3)
String(str[index])    // "S"

Teilstring

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)
String(str[startIndex...endIndex])     // "Strin"

Erste n Zeichen

let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[..<startIndex])    // "My "

Letzte n Zeichen

let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[startIndex...])    // "String"

Swift 2 und 3

str = "My String"

** String am Index **

Swift 2

let charAtIndex = String(str[str.startIndex.advancedBy(3)])  // charAtIndex = "S"

Swift 3

str[str.index(str.startIndex, offsetBy: 3)]

SubString fromIndex toIndex

Swift 2

let subStr = str[str.startIndex.advancedBy(3)...str.startIndex.advancedBy(7)] // subStr = "Strin"

Swift 3

str[str.index(str.startIndex, offsetBy: 3)...str.index(str.startIndex, offsetBy: 7)]

Erste n Zeichen

let first2Chars = String(str.characters.prefix(2)) // first2Chars = "My"

Letzte n Zeichen

let last3Chars = String(str.characters.suffix(3)) // last3Chars = "ing"
Warif Akhand Rishi
quelle
24

Swift 2.0 ab Xcode 7 GM Seed

var text = "Hello, world!"

let firstChar = text[text.startIndex.advancedBy(0)] // "H"

Ersetzen Sie für das n-te Zeichen 0 durch n-1.

Bearbeiten: Swift 3.0

text[text.index(text.startIndex, offsetBy: 0)]


nb Es gibt einfachere Möglichkeiten, bestimmte Zeichen in der Zeichenfolge zu erfassen

z.B let firstChar = text.characters.first

Matt Le Fleur
quelle
23

Wenn Sie sehen, Cannot subscript a value of type 'String'...verwenden Sie diese Erweiterung:

Swift 3

extension String {
    subscript (i: Int) -> Character {
        return self[self.characters.index(self.startIndex, offsetBy: i)]
    }

    subscript (i: Int) -> String {
        return String(self[i] as Character)
    }

    subscript (r: Range<Int>) -> String {
        let start = index(startIndex, offsetBy: r.lowerBound)
        let end = index(startIndex, offsetBy: r.upperBound)
        return self[start..<end]
    }

    subscript (r: ClosedRange<Int>) -> String {
        let start = index(startIndex, offsetBy: r.lowerBound)
        let end = index(startIndex, offsetBy: r.upperBound)
        return self[start...end]
    }
}

Swift 2.3

extension String {
    subscript(integerIndex: Int) -> Character {
        let index = advance(startIndex, integerIndex)
        return self[index]
    }

    subscript(integerRange: Range<Int>) -> String {
        let start = advance(startIndex, integerRange.startIndex)
        let end = advance(startIndex, integerRange.endIndex)
        let range = start..<end
        return self[range]
    }
}

Quelle: http://oleb.net/blog/2014/07/swift-strings/

SoftDesigner
quelle
19

Swift 2.2 Lösung:

Die folgende Erweiterung funktioniert in Xcode 7, dies ist eine Kombination aus dieser Lösung und der Swift 2.0-Syntaxkonvertierung.

extension String {
    subscript(integerIndex: Int) -> Character {
        let index = startIndex.advancedBy(integerIndex)
        return self[index]
    }

    subscript(integerRange: Range<Int>) -> String {
        let start = startIndex.advancedBy(integerRange.startIndex)
        let end = startIndex.advancedBy(integerRange.endIndex)
        let range = start..<end
        return self[range]
    }
}
Dan Beaulieu
quelle
13

Die Swift-String-Klasse bietet aufgrund ihrer nativen Unterstützung für UTF-Zeichen keine Möglichkeit, ein Zeichen an einem bestimmten Index abzurufen. Die variable Länge eines UTF-Zeichens im Speicher macht es unmöglich, direkt zu einem Zeichen zu springen. Das bedeutet, dass Sie die Zeichenfolge jedes Mal manuell durchlaufen müssen.

Sie können String erweitern, um eine Methode bereitzustellen, die die Zeichen bis zu Ihrem gewünschten Index durchläuft

extension String {
    func characterAtIndex(index: Int) -> Character? {
        var cur = 0
        for char in self {
            if cur == index {
                return char
            }
            cur++
        }
        return nil
    }
}

myString.characterAtIndex(0)!
Drawag
quelle
3
Sie können bereits Zeichenfolgen durchlaufen: für Buchstaben in "foo" {println (Buchstabe)}
Doobeh
@ Doobeh Ich meinte Schleife durch und Rückgabe des tatsächlichen Charakters wie in meiner Bearbeitung oben
Drawag
nett! Es ist lustig, wie man es durchlaufen kann, aber nicht über einen Index. Swift fühlt sich pythonisch, aber mit härteren Kanten.
Doobeh
Ich fand mit dem myString.bridgeToObjectiveC().characterAtIndex(0)oder (string as NSString ).characterAtIndex(0) gibt einen Int-Wert des Zeichens zurück
markhunte
4
Verwenden Sie keineNSString Methoden für den Zugriff auf einzelne Zeichen von einem Swift-Native aus String- die beiden verwenden unterschiedliche Zählmechanismen, sodass Sie mit höheren Unicode-Zeichen unvorhersehbare Ergebnisse erzielen. Die erste Methode sollte sicher sein (sobald die Unicode-Fehler von Swift behoben sind).
Nate Cook
11

Nebenbei bemerkt, es gibt einige Funktionen, die direkt auf die Zeichenkettendarstellung eines Strings anwendbar sind:

var string = "Hello, playground"
let firstCharacter = string.characters.first // returns "H"
let lastCharacter = string.characters.last // returns "d"

Das Ergebnis ist vom Typ Character, aber Sie können es in einen String umwandeln.

Oder dieses:

let reversedString = String(string.characters.reverse())
// returns "dnuorgyalp ,olleH" 

:-)

Frédéric Adda
quelle
11

Swift 4

String(Array(stringToIndex)[index]) 

Dies ist wahrscheinlich der beste Weg, um dieses Problem einmalig zu lösen. Sie möchten den String wahrscheinlich zuerst als Array und dann erneut als String umwandeln. Andernfalls wird anstelle eines Strings ein Zeichen zurückgegeben.

Beispiel String(Array("HelloThere")[1])gibt "e" als String zurück.

(Array("HelloThere")[1] gibt "e" als Zeichen zurück.

Swift erlaubt es nicht, Strings wie Arrays zu indizieren, aber dies erledigt den Job im Brute-Force-Stil.

Harsh G.
quelle
1
Das Duplizieren des gesamten Zeichenfolgeninhalts an einen anderen Speicherort ist insbesondere bei großen Zeichenfolgen kontraproduktiv. Für einfache Aufgaben wie den direkten Speicherzugriff sollten keine zusätzlichen Speicherzuordnungen erforderlich sein.
Cristik
7

Meine sehr einfache Lösung:

Swift 4.1:

let myString = "Test string"
let index = 0
let firstCharacter = myString[String.Index(encodedOffset: index)]

Swift 5.1:

let firstCharacter = myString[String.Index.init(utf16Offset: index, in: myString)]
Linh Dao
quelle
Funktioniert in Swift 4.1
Leanne
Einfachste Lösung und jetzt mit Swift 5 Beispiel :)
OhadM
@Linh Dao Verwenden Sie encodedOffset nicht. encodedOffset ist veraltet: encodedOffset ist veraltet, da die häufigste Verwendung falsch ist.
Leo Dabus
@OhadM am einfachsten bedeutet nicht, dass es korrekt ist oder zumindest nicht wie erwartet funktioniert. Versuchen Sie es let flags = "🇺🇸🇧🇷" flags[String.Index(utf16Offset: 4, in: flags)] // "🇧🇷"
Leo Dabus
1
@OhadM Mein Kommentar war nur eine Warnung. Sie können es verwenden, wenn Sie der Meinung sind, dass es sich so verhält, wie Sie es erwarten.
Leo Dabus
6

Sie können dies tun, indem Sie String in Array konvertieren und ihn anhand eines Index gemäß dem folgenden Index abrufen

var str = "Hello"
let s = Array(str)[2]
print(s)
Soeng Saravit
quelle
1
Beachten Sie, dass diese Lösung zu einer Duplizierung des Inhalts führt, wodurch der Speicher und die CPU weniger leistungsfähig sind.
Cristik
5

Ich hatte gerade das gleiche Problem. Mach das einfach:

var aString: String = "test"
var aChar:unichar = (aString as NSString).characterAtIndex(0)
user3723247
quelle
Dies schlägt für viele Emoji und andere Charaktere fehl, die tatsächlich mehr als ein "Zeichen" in einem aufnehmen NSString.
rmaddy
5

Swift3

Sie können die tiefgestellte Syntax verwenden, um auf das Zeichen an einem bestimmten Zeichenfolgenindex zuzugreifen.

let greeting = "Guten Tag!"
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index] // a

Besuch https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html

oder wir können eine String-Erweiterung in Swift 4 durchführen

extension String {
    func getCharAtIndex(_ index: Int) -> Character {
        return self[self.index(self.startIndex, offsetBy: index)]
    }
}

VERWENDUNGSZWECK:

let foo = "ABC123"
foo.getCharAtIndex(2) //C
Hamed
quelle
3

Meine Lösung ist in einer Zeile, vorausgesetzt, Kadena ist die Zeichenfolge und 4 ist die n-te Position, die Sie möchten:

let character = cadena[advance(cadena.startIndex, 4)]

Einfach ... Ich nehme an, Swift wird in zukünftigen Versionen mehr über Teilzeichenfolgen enthalten.

Julio César Fernández Muñoz
quelle
1
Ist das nicht dasselbe wie var charAtIndex = string[advance(string.startIndex, 10)]in Sulthans Antwort?
Martin R
Ja, es ist die gleiche Lösung mit einem anderen Beispiel, wie es Sulthan gesagt hat. Entschuldigung für das Duplikat. :) Es ist einfach, dass zwei Leute den gleichen Weg gefunden haben.
Julio César Fernández Muñoz
3

Swift 3: eine andere Lösung (auf dem Spielplatz getestet)

extension String {
    func substr(_ start:Int, length:Int=0) -> String? {
        guard start > -1 else {
            return nil
        }

        let count = self.characters.count - 1

        guard start <= count else {
            return nil
        }

        let startOffset = max(0, start)
        let endOffset = length > 0 ? min(count, startOffset + length - 1) : count

        return self[self.index(self.startIndex, offsetBy: startOffset)...self.index(self.startIndex, offsetBy: endOffset)]
    }
}

Verwendungszweck:

let txt = "12345"

txt.substr(-1) //nil
txt.substr(0) //"12345"
txt.substr(0, length: 0) //"12345"
txt.substr(1) //"2345"
txt.substr(2) //"345"
txt.substr(3) //"45"
txt.substr(4) //"5"
txt.substr(6) //nil
txt.substr(0, length: 1) //"1"
txt.substr(1, length: 1) //"2"
txt.substr(2, length: 1) //"3"
txt.substr(3, length: 1) //"4"
txt.substr(3, length: 2) //"45"
txt.substr(3, length: 3) //"45"
txt.substr(4, length: 1) //"5"
txt.substr(4, length: 2) //"5"
txt.substr(5, length: 1) //nil
txt.substr(5, length: -1) //nil
txt.substr(-1, length: -1) //nil
Peter Kreinz
quelle
2

Update für Swift 2.0 SubString

public extension String {
    public subscript (i: Int) -> String {
        return self.substringWithRange(self.startIndex..<self.startIndex.advancedBy(i + 1))
    }

    public subscript (r: Range<Int>) -> String {
        get {
            return self.substringWithRange(self.startIndex.advancedBy(r.startIndex)..<self.startIndex.advancedBy(r.endIndex))
        }
    }

}
YannSteph
quelle
2

Ich denke, dass eine schnelle Antwort für den ersten Charakter sein könnte:

let firstCharacter = aString[aString.startIndex]

Es ist so viel eleganter und leistungsfähiger als:

let firstCharacter = Array(aString.characters).first

Aber ... wenn Sie manipulieren und mehr Operationen mit Zeichenfolgen ausführen möchten, könnten Sie denken, dass Sie eine Erweiterung erstellen. Es gibt eine Erweiterung mit diesem Ansatz, die der bereits hier veröffentlichten ziemlich ähnlich ist:

extension String {
var length : Int {
    return self.characters.count
}

subscript(integerIndex: Int) -> Character {
    let index = startIndex.advancedBy(integerIndex)
    return self[index]
}

subscript(integerRange: Range<Int>) -> String {
    let start = startIndex.advancedBy(integerRange.startIndex)
    let end = startIndex.advancedBy(integerRange.endIndex)
    let range = start..<end
    return self[range]
}

}}

ABER ES IST EINE SCHRECKLICHE IDEE !!

Die Erweiterung unten ist schrecklich ineffizient. Jedes Mal, wenn auf eine Zeichenfolge mit einer Ganzzahl zugegriffen wird, wird eine O (n) -Funktion zum Vorrücken des Startindex ausgeführt. Das Ausführen einer linearen Schleife innerhalb einer anderen linearen Schleife bedeutet, dass diese for-Schleife versehentlich O (n2) ist. Mit zunehmender Länge der Zeichenfolge nimmt die Zeit, die diese Schleife benötigt, quadratisch zu.

Stattdessen können Sie die Zeichenfolgensammlung der Zeichen verwenden.

ignaciohugog
quelle
2

Swift 3

extension String {

    public func charAt(_ i: Int) -> Character {
        return self[self.characters.index(self.startIndex, offsetBy: i)]
    }

    public subscript (i: Int) -> String {
        return String(self.charAt(i) as Character)
    }

    public subscript (r: Range<Int>) -> String {
        return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
    }

    public subscript (r: CountableClosedRange<Int>) -> String {
        return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
    }

}

Verwendungszweck

let str = "Hello World"
let sub = str[0...4]

Hilfreiche Tipps und Tricks zur Programmierung (von mir geschrieben)

sehr schön
quelle
2

Index abrufen und festlegen (String & Teilzeichenfolge) - Swift 4.2

Swift 4.2, Xcode 10

Ich habe meine Antwort auf die Antwort von @alecarlson gestützt . Der einzige große Unterschied besteht darin, dass Sie eine Substringoder eine StringRückgabe (und in einigen Fällen eine einzelne Character) erhalten können. Sie können auch getund setden Index. Schließlich ist meine etwas umständlicher und länger als die Antwort von @alecarlson , und als solche schlage ich vor, dass Sie sie in eine Quelldatei einfügen .


Erweiterung:

public extension String {
    public subscript (i: Int) -> Character {
        get {
            return self[index(startIndex, offsetBy: i)]
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ..< end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ... end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ..< end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }

    public subscript (i: Int) -> String {
        get {
            return "\(self[index(startIndex, offsetBy: i)])"
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            self.replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ..< end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ... end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ..< end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }

    public subscript (i: Int) -> Substring {
        get {
            return Substring("\(self[index(startIndex, offsetBy: i)])")
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
}
public extension Substring {
    public subscript (i: Int) -> Character {
        get {
            return self[index(startIndex, offsetBy: i)]
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }

    }
    public subscript (bounds: CountableRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ..< end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ... end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ..< end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    public subscript (i: Int) -> String {
        get {
            return "\(self[index(startIndex, offsetBy: i)])"
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ..< end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ... end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ..< end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }

    public subscript (i: Int) -> Substring {
        get {
            return Substring("\(self[index(startIndex, offsetBy: i)])")
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
}
Noah Wilder
quelle
Dadurch werden beide Indizes (Start und Ende) unnötigerweise vom startIndex versetzt. Sie können den Endindex einfach mit range.count versetzen und den Startindex versetzen
Leo Dabus
2

Swift 4.2

Diese Antwort ist ideal, weil sie Stringalle Subsequences( Substring) in einer Erweiterung erweitert

public extension StringProtocol {

    public subscript (i: Int) -> Element {
        return self[index(startIndex, offsetBy: i)]
    }

    public subscript (bounds: CountableClosedRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[start...end]
    }

    public subscript (bounds: CountableRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[start..<end]
    }

    public subscript (bounds: PartialRangeUpTo<Int>) -> SubSequence {
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[startIndex..<end]
    }

    public subscript (bounds: PartialRangeThrough<Int>) -> SubSequence {
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[startIndex...end]
    }

    public subscript (bounds: CountablePartialRangeFrom<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        return self[start..<endIndex]
    }
}

Verwendungszweck

var str = "Hello, playground"

print(str[5...][...5][0])
// Prints ","
Noah Wilder
quelle
Dies versetzt beide Indizes ( startund end) unnötigerweise von der startIndex. Sie können den endIndex einfach mit dem range.count versetzen und den startIndex
versetzen
2

Inzwischen ist der Index (_ :) nicht verfügbar. Sowie wir das nicht können

str[0] 

mit string.Wir müssen "String.Index" angeben. Aber wie können wir auf diese Weise unsere eigene Indexnummer angeben, stattdessen können wir verwenden:

string[str.index(str.startIndex, offsetBy: 0)]
Yodagama
quelle
Bitte bearbeiten Sie Ihre Antwort und fügen Sie einen Kontext hinzu, indem Sie erklären, wie Ihre Antwort das Problem löst, anstatt nur eine Code-Antwort zu veröffentlichen. Aus dem Rückblick
Pedram Parsian
Warum das unnötige Versetzen? Warum nicht einfach string[string.startIndex]? Übrigens würde sich der Code nicht korrekt verhalten / kompilieren, da Sie zwei verschiedene Variablennamen verwendet haben.
Cristik
2

Swift 4.2 oder höher

Bereichs- und Teilbereichsabonnement mit String'sindices Eigenschaft

Als Variation der netten Antwort von @LeoDabus können wir eine zusätzliche Erweiterung hinzufügen, um DefaultIndicesbei der Implementierung der benutzerdefinierten Indizes (nach speziellen Bereichen und Teilbereichen) für letztere auf die indicesEigenschaft zurückgreifen zu können .StringInt

extension DefaultIndices {
    subscript(at: Int) -> Elements.Index { index(startIndex, offsetBy: at) }
}

// Moving the index(_:offsetBy:) to an extension yields slightly
// briefer implementations for these String extensions.
extension String {
    subscript(range: Range<Int>) -> SubSequence {
        let start = indices[range.lowerBound]
        return self[start..<indices[start...][range.count]]
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        let start = indices[range.lowerBound]
        return self[start...indices[start...][range.count]]
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence {
        self[indices[range.lowerBound]...]
    }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence {
        self[...indices[range.upperBound]]
    }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence {
        self[..<indices[range.upperBound]]
    }
}

let str = "foo bar baz bax"
print(str[4..<6]) // "ba"
print(str[4...6]) // "bar"
print(str[4...])  // "bar baz bax"
print(str[...6])  // "foo bar"
print(str[..<6])  // "foo ba"

Vielen Dank an @LeoDabus für den Hinweis, dass ich die indicesEigenschaft als (andere) Alternative zum StringAbonnieren verwenden soll!

dfri
quelle
1
Der einzige Nachteil ist, dass CountableClosedRange beide Indizes vom startIndex
Leo Dabus
1
@ LeoDabus Ich verstehe. Ja, meistens Linux, aber heutzutage nicht viel Swift: / Ich benutze es, swiftenvwenn ich es tue, aber ich denke, es wird dann auch etwas bald mit 4.2 aktualisiert.
dfri
1
@LeoDabus danke für die Aktualisierung dieser Antwort auf das moderne Swift!
27.
1
@LeoDabus gute Arbeit! Ich werde mich zu einem späteren Zeitpunkt mit den Details befassen müssen, aber ich erinnere mich, dass es mir nie gefallen hat, dass wir einige der geordneten / zählbaren Set-Typen auf Foundation zurückgreifen mussten.
dfri
1
Danke Kumpel!!!
Leo Dabus
2

Swift 5.1.3:

Fügen Sie eine String-Erweiterung hinzu:

extension String {

 func stringAt(_ i: Int) -> String { 
   return String(Array(self)[i]) 
 } 

 func charAt(_ i: Int) -> Character { 
  return Array(self)[i] 
 } 
}

let str = "Teja Kumar"
let str1: String = str.stringAt(2)  //"j"
let str2: Character = str.charAt(5)  //"k"
Teja Kumar Bethina
quelle
1
Dadurch wird die gesamte Zeichenfolge jedes Mal in ein Array von Zeichen konvertiert, wenn Sie diese Eigenschaft aufrufen, um ein einzelnes Zeichen daraus zu extrahieren.
Leo Dabus
1

Swifts StringTyp bietet keinecharacterAtIndex Methode, da eine Unicode-Zeichenfolge auf verschiedene Arten codiert werden kann. Gehst du mit UTF8, UTF16 oder etwas anderem?

Sie können auf die CodeUnitSammlungen zugreifen, indem Sie die Eigenschaften String.utf8und String.utf16abrufen. Sie können auch auf die UnicodeScalarSammlung zugreifen, indem Sie die abrufenString.unicodeScalars Eigenschaft .

Im Geiste der NSStringImplementierung gebe ich einen unicharTyp zurück.

extension String
{
    func characterAtIndex(index:Int) -> unichar
    {
        return self.utf16[index]
    }

    // Allows us to use String[index] notation
    subscript(index:Int) -> unichar
    {
        return characterAtIndex(index)
    }
}

let text = "Hello Swift!"
let firstChar = text[0]
Erik
quelle
Dies schlägt bei Zeichen fehl, die mehr Speicher als 16 Bit benötigen. Grundsätzlich jedes Unicode-Zeichen jenseits von U + FFFF.
rmaddy
1

Um das Thema zu füttern und schnelle Indexmöglichkeiten aufzuzeigen, finden Sie hier eine kleine Zeichenfolge, die auf dem Index "substring-toolbox" basiert

Diese Methoden sind sicher und gehen niemals über String-Indizes

extension String {
    // string[i] -> one string char
    subscript(pos: Int) -> String { return String(Array(self)[min(self.length-1,max(0,pos))]) }

    // string[pos,len] -> substring from pos for len chars on the left
    subscript(pos: Int, len: Int) -> String { return self[pos, len, .pos_len, .left2right] }

    // string[pos, len, .right2left] -> substring from pos for len chars on the right
    subscript(pos: Int, len: Int, way: Way) -> String { return self[pos, len, .pos_len, way] }

    // string[range] -> substring form start pos on the left to end pos on the right
    subscript(range: Range<Int>) -> String { return self[range.startIndex, range.endIndex, .start_end, .left2right] }

    // string[range, .right2left] -> substring start pos on the right to end pos on the left
    subscript(range: Range<Int>, way: Way) -> String { return self[range.startIndex, range.endIndex, .start_end, way] }

    var length: Int { return countElements(self) }
    enum Mode { case pos_len, start_end }
    enum Way { case left2right, right2left }
    subscript(var val1: Int, var val2: Int, mode: Mode, way: Way) -> String {
        if mode == .start_end {
            if val1 > val2 { let val=val1 ; val1=val2 ; val2=val }
            val2 = val2-val1
        }
        if way == .left2right {
            val1 = min(self.length-1, max(0,val1))
            val2 = min(self.length-val1, max(1,val2))
        } else {
            let val1_ = val1
            val1 = min(self.length-1, max(0, self.length-val1_-val2 ))
            val2 = max(1, (self.length-1-val1_)-(val1-1) )
        }
        return self.bridgeToObjectiveC().substringWithRange(NSMakeRange(val1, val2))

        //-- Alternative code without bridge --
        //var range: Range<Int> = pos...(pos+len-1)
        //var start = advance(startIndex, range.startIndex)
        //var end = advance(startIndex, range.endIndex)
        //return self.substringWithRange(Range(start: start, end: end))
    }
}


println("0123456789"[3]) // return "3"

println("0123456789"[3,2]) // return "34"

println("0123456789"[3,2,.right2left]) // return "56"

println("0123456789"[5,10,.pos_len,.left2right]) // return "56789"

println("0123456789"[8,120,.pos_len,.right2left]) // return "01"

println("0123456789"[120,120,.pos_len,.left2right]) // return "9"

println("0123456789"[0...4]) // return "01234"

println("0123456789"[0..4]) // return "0123"

println("0123456789"[0...4,.right2left]) // return "56789"

println("0123456789"[4...0,.right2left]) // return "678" << because ??? range can wear endIndex at 0 ???
Luc-Olivier
quelle
1

Eine Python-ähnliche Lösung, mit der Sie einen negativen Index verwenden können.

var str = "Hello world!"
str[-1]        // "!"

könnte sein:

extension String {
    subscript (var index:Int)->Character{
        get {
            let n = distance(self.startIndex, self.endIndex)
            index %= n
            if index < 0 { index += n }
            return self[advance(startIndex, index)]
        }
    }
}

Übrigens kann es sich lohnen, die Slice-Notation der gesamten Python zu transponieren

Joseph Merdrignac
quelle
Haben Sie etwas dagegen, etwas zu schreiben, das für Swift 4 kompiliert wird? letzte Rückkehr ... Zeile scheint nicht zu funktionieren Die Funktion progress () ist nicht da, glaube ich.
C0D3
1

Sie können String auch wie folgt in ein Array von Zeichen konvertieren:

let text = "My Text"
let index = 2
let charSequence = text.unicodeScalars.map{ Character($0) }
let char = charSequence[index]

Dies ist der Weg, um char in konstanter Zeit am angegebenen Index zu erhalten.

Das folgende Beispiel läuft nicht in konstanter Zeit, sondern erfordert lineare Zeit. Wenn Sie in String nach Index viel suchen, verwenden Sie die oben beschriebene Methode.

let char = text[text.startIndex.advancedBy(index)]
Marcin Kapusta
quelle