Uzyskaj n-ty znak ciągu w języku programowania Swift

419

Jak mogę uzyskać n-ty znak ciągu? Próbowałem []akcesorium brack ( ) bez powodzenia.

var string = "Hello, world!"

var firstChar = string[0] // Throws error

BŁĄD: „indeks dolny” jest niedostępny: nie można subskrybować ciągu znaków z Int, patrz komentarz do dokumentacji do dyskusji

Mohsen
źródło
1
Komunikat o błędzie „Nie można subskrybować ciągu za pomocą int, patrz komentarz do dokumentacji do dyskusji” wydaje się odnosić do github.com/apple/swift/blob/master/stdlib/public/core/…
andrewdotn
użyj var firstChar = string.index(string.startIndex, offsetBy: 0)zamiast
Sazzad Hissain Khan
@SazzadHissainKhan spowodowałoby to indeks ciągu, a nie znak. A właściwie dlaczego nie po prostu string.startIndex? Dla pierwszej postaci string[string.startIndex] lub po prostu string.first. Zauważ, że pierwsze podejście, które musisz sprawdzić, czy łańcuch jest pusty, drugie zwraca opcjonalne
Leo Dabus

Odpowiedzi:

567

Uwaga: zapoznaj się z odpowiedzią Leo Dabusa dotyczącą prawidłowej implementacji Swift 4 i Swift 5.

Swift 4 lub nowszy

Ten Substringtyp został wprowadzony w Swift 4, aby podciągi były szybsze i bardziej wydajne poprzez współdzielenie pamięci z oryginalnym ciągiem, więc to właśnie powinny zwrócić funkcje indeksu dolnego.

Wypróbuj tutaj

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

Aby przekonwertować Substringna a String, możesz po prostu to zrobić String(string[0..2]), ale powinieneś to zrobić tylko wtedy, gdy planujesz zachować podciąg. W przeciwnym razie bardziej efektywne jest utrzymanie go Substring.

Byłoby wspaniale, gdyby ktoś wymyślił dobry sposób na połączenie tych dwóch rozszerzeń w jedno. Próbowałem rozszerzyć StringProtocol bez powodzenia, ponieważ indexmetoda tam nie istnieje. Uwaga: Ta odpowiedź została już zredagowana, jest poprawnie zaimplementowana i teraz działa również dla podciągów. Upewnij się, że używasz prawidłowego zakresu, aby uniknąć awarii podczas subskrybowania typu StringProtocol. Do indeksowania z zakresem, który nie spowoduje awarii przy wartościach spoza zakresu, możesz użyć tej implementacji


Dlaczego to nie jest wbudowane?

Komunikat o błędzie mówi „zobacz komentarz do dokumentacji do dyskusji” . Apple udostępnia następujące wyjaśnienie w pliku UnavailableStringAPIs.swift :

Ciągi indeksowania z liczbami całkowitymi nie są dostępne.

Pojęcie „ ith znak w ciągu” ma różne interpretacje w różnych bibliotekach i komponentach systemu. Prawidłową interpretację należy wybrać zgodnie z przypadkiem użycia i zaangażowanymi interfejsami API, więc String nie można zapisać jej za pomocą liczby całkowitej.

Swift zapewnia kilka różnych sposobów dostępu do danych znakowych przechowywanych w łańcuchach.

  • String.utf8to zbiór jednostek kodu UTF-8 w ciągu. Użyj tego interfejsu API podczas konwersji ciągu znaków na UTF-8. Większość interfejsów API POSIX przetwarza ciągi znaków w kategoriach jednostek kodu UTF-8.

  • String.utf16to zbiór jednostek kodu UTF-16 w postaci ciągu. Większość interfejsów API kakao i kakao dotykowych przetwarza ciągi znaków pod względem jednostek kodu UTF-16. Na przykład przypadki NSRangeużycia NSAttributedStringi NSRegularExpressionprzechowywania przesunięć i długości podciągów w odniesieniu do jednostek kodu UTF-16.

  • String.unicodeScalarsto zbiór skalarów Unicode. Użyj tego interfejsu API podczas wykonywania niskopoziomowych manipulacji danymi znaków.

  • String.characters to zbiór rozszerzonych klastrów grafemowych, które są przybliżeniem znaków postrzeganych przez użytkownika.

Należy pamiętać, że podczas przetwarzania ciągów zawierających tekst czytelny dla człowieka należy unikać przetwarzania znaków po znaku w największym możliwym stopniu. Stosowanie wysokiego poziomu locale wrażliwe algorytmy Unicode zamiast, na przykład String.localizedStandardCompare(), String.localizedLowercaseString, String.localizedStandardRangeOfString()etc.

aleclarson
źródło
4
Cannot find an overload for 'advance' that accepts an argument list of type '(String.Index, T)'... String.Indexi Intnie są kompatybilne.
7
Jeśli widzisz, Cannot subscript a value of type 'String'...sprawdź tę odpowiedź: stackoverflow.com/a/31265316/649379
SoftDesigner
24
Kiedy próbuję tego użyć, rozumiem Ambiguous use of 'subscript'.
jowie
18
OSTRZEŻENIE! Poniższe rozszerzenie jest strasznie nieefektywne. Za każdym razem, gdy ciąg jest uzyskiwany za pomocą liczby całkowitej, uruchamiana jest funkcja O (n) w celu przyspieszenia początkowego indeksu. Uruchomienie pętli liniowej wewnątrz innej pętli liniowej oznacza, że ​​przypadkowo pętla ta ma wartość O (n2) - wraz ze wzrostem długości łańcucha rośnie czas kwadratowy tej pętli. Zamiast tego możesz użyć kolekcji ciągów znaków.
ignaciohugog
2
fatal error: Can't form a Character from an empty String
Devin B
341

Szybki 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"

Musisz dodać to rozszerzenie String do swojego projektu (jest w pełni przetestowane):

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

Mimo że Swift zawsze miał gotowe rozwiązanie tego problemu (bez rozszerzenia String, które podałem poniżej), nadal zdecydowanie polecam użycie rozszerzenia. Dlaczego? Ponieważ zaoszczędziło mi to dziesiątki godzin bolesnej migracji z wczesnych wersji Swift, gdzie składnia String zmieniała się prawie w każdym wydaniu, ale wszystko, co musiałem zrobić, to zaktualizować implementację rozszerzenia w przeciwieństwie do refaktoryzacji całego projektu. Dokonaj swojego wyboru.

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
źródło
zmień range.upperBound - range.lowerBoundnarange.count
Leo Dabus
To nie jest część pierwotnego pytania, ale ... byłoby miło, gdyby to również obsługiwane zadanie. Np. S [i] = "a" :).
Chris Prince
2
Uważam, że indeksy Swift 4.2 nie są ponownie dostępne.
Pojawia
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,
To powinny być wbudowane funkcje
Ammar Mujeeb
150

Właśnie wymyśliłem to fajne obejście

var firstChar = Array(string)[0]
Jens Wirth
źródło
2
Jest to dobre szybkie obejście (zwykłego) przypadku, gdy wiesz, że masz ciągi zakodowane w UTF8 lub ASCII. Tylko upewnij się, że ciągi nigdy nie będą w kodowaniu, które wykorzystuje więcej niż jeden bajt.
Jeff Hay
47
Wydaje się to wyjątkowo nieefektywne, ponieważ kopiujesz cały ciąg znaków, aby uzyskać pierwszy znak. Użyj ciągu [ciąg. startIndex] zamiast 0, jak zauważył Sulthan.
Bjorn,
6
Rozpakuj ciąg: var firstChar = Array(string!)[0]inaczej powie toadd arrayLiteral
Mohammad Zaid Pathan
2
Nie wierzę, że to jest czyste, w rzeczywistości jest to runda. Nie jestem do końca pewien, który inicjator w tablicy jest używany jako pierwszy, co powoduje, że tak się dzieje (i zakładam, że jest to inicjalizator SequenceType, który powoduje, że gromadzi on znaki łańcucha jako poszczególne składniki tablicy). Nie jest to w ogóle jawne i może zostać poprawione w przyszłości bez rzutowania. To również nie działa, jeśli używasz skróconej tablicy dla [string] .pierwszy. @ Rozwiązanie Sulthana działa najlepiej, aby użyć upieczonych wartości indeksu. Jest o wiele bardziej jasne, co się tutaj dzieje.
TheCodingArt
2
Wow, błąd segmentu kompilatora!
Frank
124

Brak indeksowania przy użyciu liczb całkowitych, tylko przy użyciu String.Index. Głównie z liniową złożonością. Możesz także tworzyć zakresy String.Indexi uzyskiwać z nich podciągi.

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]

Pamiętaj, że nigdy nie możesz użyć indeksu (lub zakresu) utworzonego z jednego ciągu na drugi

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]
Sułtan
źródło
7
Stringindeksy są unikalne dla łańcucha. Wynika to z faktu, że różne łańcuchy mogą mieć różne wieloczęściowe UTF-16 Charactersi / lub w różnych pozycjach, więc indeksy jednostek UTF-16 nie będą pasować, mogą spaść poza koniec lub punkt wewnątrz wieloczęściowego UTF-16 Character.
zaph
@Zaph To oczywiste.
Sulthan
3
Wyjaśnienie, dlaczego mówisz: „czasami się zawiesi lub spowoduje niezdefiniowane zachowanie”. Być może lepiej powiedzieć, po prostu nie rób tego, ponieważ ...
zaph
1
@Sulthan ..jest teraz ..<(w twoim zadaniu range)
Aaron Brager
3
@CajunLuke Wiem, że minęło trochę czasu, odkąd opublikowałeś ten komentarz, ale spójrz na tę odpowiedź . Możesz użyćvar lastChar = string[string.endIndex.predecessor()]
David L
122

Xcode 11 • Swift 5.1

Możesz rozszerzyć StringProtocol, aby indeks dolny był dostępny również dla podciągów:

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]
    }
}

Testowanie

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
źródło
Czy mogę zapytać, co to jest „self [index (startIndex, offsetBy: i)]”? A jak działa „ja [i]”?
allenlinli
1
Cześć Leo, dziękuję za rozwiązanie! Właśnie (dzisiaj) przełączyłem się z Swift 2.3 na 3, a twój indeks dolny rozwiązania (zakres: Zakres <Int>) podaje błąd „Dodatkowy argument„ ograniczony przez przez wywołanie ”. Jak myślisz, co może być nie tak?
Ahmet Akkök
@ AhmetAkkök na pewno nie zmieniłeś kodu?
Leo Dabus
1
@Leo okazało się, że nie przekonwertowałem całego projektu, ale na aplikacji nie na rozszerzeniu, powtórzyłem proces zarówno dla aplikacji, jak i rozszerzenia i teraz działa OK. Twoja pomoc jest bardzo ceniona!
Ahmet Akkök
to jest bardzo skomplikowany kod. Jaka jest przewaga nad robieniem gry return String(Array(characters)[range])w Swift 3?
Dan Rosenstark,
68

Szybki 4

let str = "My String"

Ciąg o indeksie

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

Podciąg

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

Pierwsze n znaków

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

Ostatnie n znaków

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

Swift 2 i 3

str = "My String"

** Ciąg według indeksu **

Swift 2

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

Szybki 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"

Szybki 3

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

Pierwsze n znaków

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

Ostatnie n znaków

let last3Chars = String(str.characters.suffix(3)) // last3Chars = "ing"
Warif Akhand Rishi
źródło
24

Swift 2.0 od Xcode 7 GM Seed

var text = "Hello, world!"

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

Dla n-tego znaku zamień 0 na n-1.

Edycja: Swift 3.0

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


Uwaga: istnieją prostsze sposoby chwytania niektórych znaków w ciągu

na przykład let firstChar = text.characters.first

Matt Le Fleur
źródło
23

Jeśli widzisz, Cannot subscript a value of type 'String'...użyj tego rozszerzenia:

Szybki 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]
    }
}

Źródło: http://oleb.net/blog/2014/07/swift-strings/

SoftDesigner
źródło
19

Rozwiązanie Swift 2.2:

Poniższe rozszerzenie działa w Xcode 7, jest to połączenie tego rozwiązania i konwersji składni Swift 2.0.

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
źródło
14

Szybka klasa łańcuchowa nie zapewnia możliwości uzyskania znaku o określonym indeksie z uwagi na jego natywną obsługę znaków UTF. Zmienna długość znaku UTF w pamięci uniemożliwia skok bezpośrednio do znaku. Oznacza to, że za każdym razem musisz ręcznie zapętlić ciąg.

Możesz rozszerzyć String, aby zapewnić metodę, która będzie przechodzić między znakami aż do pożądanego indeksu

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
źródło
3
Możesz już przeglądać ciągi znaków: dla litery w „foo” {println (list)}
Doobeh
@Doobeh Miałem na myśli zapętlenie i zwrócenie rzeczywistej postaci, jak w mojej edycji powyżej
drewag
miły! To zabawne, jak możesz iterować przez to, ale nie przez indeks. Swift czuje się pytoniczny, ale z twardszymi krawędziami.
Doobeh
Znalazłem użycie myString.bridgeToObjectiveC().characterAtIndex(0)lub (string as NSString ).characterAtIndex(0) zwraca wartość Int znaku
markhunte
4
Nie używaj NSStringmetod dostępu do pojedynczych znaków z natywnego Swift String- oba używają różnych mechanizmów liczenia, dzięki czemu uzyskasz nieprzewidywalne wyniki przy wyższych znakach Unicode. Pierwsza metoda powinna być bezpieczna (po rozwiązaniu błędów Unicode Swift).
Nate Cook
11

Na marginesie, istnieje kilka funkcji, które można zastosować bezpośrednio do reprezentacji ciągu znaków w łańcuchu znaków, takie jak:

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

Wynik jest typu Postać, ale możesz rzucić go na Ciąg.

Albo to:

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

:-)

Frédéric Adda
źródło
11

Szybki 4

String(Array(stringToIndex)[index]) 

Jest to prawdopodobnie najlepszy sposób na rozwiązanie tego problemu jednorazowo. Prawdopodobnie chcesz najpierw rzucić String jako tablicę, a następnie ponownie rzutować wynik jako String. W przeciwnym razie znak zostanie zwrócony zamiast łańcucha.

Przykład String(Array("HelloThere")[1])zwróci „e” jako ciąg.

(Array("HelloThere")[1] zwróci „e” jako postać.

Swift nie pozwala na indeksowanie ciągów znaków jak tablic, ale wykonuje to zadanie w stylu brute-force.

Surowy G.
źródło
1
Powielanie całej zawartości łańcucha do innej lokalizacji w pamięci jest niezrównane, szczególnie w przypadku dużych łańcuchów. Nie powinniśmy potrzebować dodatkowych przydziałów pamięci dla prostych zadań, takich jak bezpośredni dostęp do pamięci.
Cristik
7

Moje bardzo proste rozwiązanie:

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
źródło
Działa w Swift 4.1
leanne
Najprostsze rozwiązanie, a teraz z przykładem Swift 5 :)
OhadM
@Lhh Dao Nie używaj zakodowanego Offset. encodedOffset jest przestarzałe: encodedOffset jest przestarzałe, ponieważ najczęściej używane użycie jest nieprawidłowe.
Leo Dabus
@OhadM najprostszy nie oznacza, że ​​jest poprawny, a przynajmniej nie będzie działał zgodnie z oczekiwaniami Spróbujlet flags = "🇺🇸🇧🇷" flags[String.Index(utf16Offset: 4, in: flags)] // "🇧🇷"
Leo Dabus
1
@OhadM Mój komentarz był tylko ostrzeżeniem. Możesz go użyć, jeśli uważasz, że zachowuje się tak, jak się spodziewasz.
Leo Dabus
6

Możesz to zrobić, konwertując String na Array i uzyskując go według określonego indeksu za pomocą indeksu dolnego jak poniżej

var str = "Hello"
let s = Array(str)[2]
print(s)
Soeng Saravit
źródło
1
Zauważ, że to rozwiązanie spowoduje duplikację zawartości, co spowoduje, że będzie mniej wydajna pod względem pamięci i procesora.
Cristik
5

Właśnie miałem ten sam problem. Po prostu zrób to:

var aString: String = "test"
var aChar:unichar = (aString as NSString).characterAtIndex(0)
użytkownik3723247
źródło
Nie udaje się to wielu Emoji i innym postaciom, które faktycznie przyjmują więcej niż jeden „znak” w NSString.
rmaddy
5

Swift3

Możesz użyć składni indeksu dolnego, aby uzyskać dostęp do znaku przy określonym indeksie ciągów.

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

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

lub możemy wykonać rozszerzenie ciągu w Swift 4

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

STOSOWANIE:

let foo = "ABC123"
foo.getCharAtIndex(2) //C
Hamed
źródło
3

Moje rozwiązanie jest w jednej linii, załóżmy, że cadena jest łańcuchem, a 4 to n-ta pozycja, którą chcesz:

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

Proste ... Przypuszczam, że Swift będzie zawierał więcej informacji na temat podciągów w przyszłych wersjach.

Julio César Fernández Muñoz
źródło
1
Czy to nie to samo, co var charAtIndex = string[advance(string.startIndex, 10)]w odpowiedzi Sulthana?
Martin R
Tak, to samo rozwiązanie z innym przykładem, jak powiedział Sulthan. Przepraszamy za duplikat. :) To proste, dwie osoby znalazły w ten sam sposób.
Julio César Fernández Muñoz
3

Swift 3: inne rozwiązanie (testowane na placu zabaw)

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

Stosowanie:

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
źródło
2

Aktualizacja do szybkiego subString 2.0

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
źródło
2

Myślę, że szybką odpowiedzią na pierwszą postać może być:

let firstCharacter = aString[aString.startIndex]

Jest o wiele bardziej elegancki i wydajny niż:

let firstCharacter = Array(aString.characters).first

Ale .. jeśli chcesz manipulować i wykonywać więcej operacji na ciągach, możesz pomyśleć o utworzeniu rozszerzenia .. istnieje jedno rozszerzenie z tym podejściem, jest dość podobne do tego, które już tu opublikowano:

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]
}

}

ALE TO OKOLICZNY POMYSŁ !!

Poniższe rozszerzenie jest strasznie nieefektywne. Za każdym razem, gdy ciąg jest uzyskiwany za pomocą liczby całkowitej, uruchamiana jest funkcja O (n) w celu przyspieszenia początkowego indeksu. Uruchomienie pętli liniowej wewnątrz innej pętli liniowej oznacza, że ​​przypadkowo pętla ta ma wartość O (n2) - wraz ze wzrostem długości łańcucha rośnie czas kwadratowy tej pętli.

Zamiast tego możesz użyć kolekcji ciągów znaków.

ignaciohugog
źródło
2

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

}

Stosowanie

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

Pomocne wskazówki i porady programistyczne (napisane przeze mnie)

cichy
źródło
2

Pobierz i ustaw indeks (ciąg i podciąg) - Swift 4.2

Swift 4.2, Xcode 10

Oparłem swoją odpowiedź na odpowiedzi @alecarlson . Jedyna różnica polega można dostać Substringlub Stringzwrócony (aw niektórych przypadkach pojedyncza Character). Możesz także geti setindeks dolny. Wreszcie moje jest trochę bardziej kłopotliwe i dłuższe niż odpowiedź @alecarlson i jako takie sugeruję, abyś umieścił je w pliku źródłowym.


Rozbudowa:

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
źródło
Jest to niepotrzebnie równoważy oba indeksy (start i end) z startIndex. Możesz po prostu skompensować indeks końcowy za pomocą range.count i przesunąć indeks początkowy
Leo Dabus
2

Szybki 4.2

Ta odpowiedź jest idealna, ponieważ rozszerza Stringi wszystkie jej Subsequences( Substring) w jednym rozszerzeniu

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]
    }
}

Stosowanie

var str = "Hello, playground"

print(str[5...][...5][0])
// Prints ","
Noah Wilder
źródło
Jest to niepotrzebnie równoważące oba indeksy ( starti end) z startIndex. Możesz po prostu skompensować endindeks za pomocą range.count i przesunąć startindeks
Leo Dabus
2

Do tej pory indeks dolny (_ :) jest niedostępny. Nie możemy tego zrobić

str[0] 

za pomocą ciągu znaków. Musimy podać ciąg „String.Index”. Ale w jaki sposób możemy w ten sposób podać własny numer indeksu, zamiast tego możemy użyć,

string[str.index(str.startIndex, offsetBy: 0)]
Jodagama
źródło
Zmodyfikuj swoją odpowiedź i dodaj kontekst, wyjaśniając, w jaki sposób rozwiązuje problem, zamiast publikować tylko odpowiedź na kod. Z recenzji
Pedram Parsian
Po co robić niepotrzebne przesunięcie? Dlaczego nie po prostu string[string.startIndex]? BTW, kod nie zachowuje się poprawnie / kompiluje, ponieważ użyłeś dwóch różnych nazw zmiennych.
Cristik
2

Swift 4.2 lub nowszy

Indeksowanie zakresu i częściowego zakresu za pomocą String'sindices właściwość

Jako wariant ładnej odpowiedzi @LeoDabus , możemy dodać dodatkowe rozszerzenie DefaultIndicesw celu umożliwienia nam powrotu do indiceswłaściwości Stringprzy wdrażaniu niestandardowych indeksów dolnych (według Intzakresów specjalistycznych i zakresów częściowych) dla tego drugiego.

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"

Dzięki @LeoDabus za wskazanie mnie w kierunku wykorzystania tej indiceswłaściwości jako (innej) alternatywy dla Stringsubskrypcji!

dfri
źródło
1
jedyną wadą jest CountableClosedRange zrównoważy oba indeksy od startIndex
Leo Dabus
1
@LeoDabus Rozumiem. Tak, głównie linux, ale obecnie niewiele Swift: / Używam, swiftenvkiedy to robię, ale myślę, że wkrótce zostanie zaktualizowany do wersji 4.2.
dfri
1
@LeoDabus dzięki za aktualizację tej odpowiedzi do nowoczesnego Swift!
dfri
1
@LeoDabus dobra robota! Będę musiał później przyjrzeć się szczegółom, ale pamiętam, że nigdy nie podobało mi się, że musieliśmy polegać na Foundation niektórych uporządkowanych / policzalnych typów zestawów.
dfri
1
Dzięki stary!!!
Leo Dabus
2

Swift 5.1.3:

Dodaj rozszerzenie String:

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
źródło
1
Spowoduje to konwersję całego łańcucha znaków na tablicę znaków za każdym razem, gdy wywołasz tę właściwość, aby wyodrębnić z niej pojedynczy znak.
Leo Dabus
1

StringTyp Swift nie zapewnia znakucharacterAtIndex metody, ponieważ istnieje kilka sposobów kodowania łańcucha Unicode. Czy korzystasz z UTF8, UTF16 lub czegoś innego?

Możesz uzyskać dostęp do CodeUnitkolekcji, pobierając właściwości String.utf8i String.utf16. Możesz również uzyskać dostęp do UnicodeScalarkolekcji, pobierając plikString.unicodeScalars właściwość.

W duchu NSStringimplementacji zwracam unichartyp.

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
źródło
Nie powiedzie się to dla znaków, które wymagają więcej miejsca niż 16 bitów. Zasadniczo każdy znak Unicode poza U + FFFF.
rmaddy
1

Aby nakarmić temat i pokazać szybkie możliwości indeksu dolnego, oto krótki ciąg oparty na indeksie dolnym

Te metody są bezpieczne i nigdy nie przekraczają indeksów ciągów

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
źródło
1

Rozwiązanie typu python, które pozwala na użycie indeksu ujemnego,

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

możliwe:

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

Nawiasem mówiąc, może być warto przetransponować notację wycinka całego pytona

Joseph Merdrignac
źródło
Czy masz coś przeciwko napisaniu czegoś, co skompiluje się w Swift 4? ostatni powrót ... wydaje się, że nie działa funkcja line (), że tam nie ma.
C0D3,
1

Możesz także przekonwertować ciąg znaków na tablicę znaków w następujący sposób:

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

Jest to sposób na uzyskanie znaku w określonym indeksie w stałym czasie.

Poniższy przykład nie działa w stałym czasie, ale wymaga czasu liniowego. Więc jeśli masz dużo wyszukiwania w Ciąg według indeksu, użyj powyższej metody.

let char = text[text.startIndex.advancedBy(index)]
Marcin Kapusta
źródło