Jak działa podciąg String w Swift

354

Aktualizowałem część mojego starego kodu i odpowiedzi w Swift 3, ale kiedy dotarłem do Swift Strings i indeksowania z podciągami, sprawy się skomplikowały.

W szczególności próbowałem:

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5)
let prefix = str.substringWithRange(prefixRange)

gdzie druga linia dawała mi następujący błąd

Wartość typu „String” nie ma elementu „substringWithRange”

Widzę, że Stringma teraz następujące metody:

str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)

Na początku były to naprawdę mylące, więc zacząłem grać wokół indeksu i zasięgu . To jest kolejne pytanie i odpowiedź na podciąg. Dodaję odpowiedź poniżej, aby pokazać, jak są one używane.

Suragch
źródło
2
Dla tych, którzy chcą pobrać podciąg z ciągu stackoverflow.com/q/32305891/468724
Inder Kumar Rathore
lub dolny ciąg lub podciąg stackoverflow.com/questions/24092884/...
Leo Dabus

Odpowiedzi:

831

wprowadź opis zdjęcia tutaj

Zastosowano wszystkie poniższe przykłady

var str = "Hello, playground"

Szybki 4

Ciągi zostały dość gruntownie zmienione w Swift 4. Kiedy teraz otrzymujesz podciąg z String, dostajesz z Substringpowrotem typ zamiast a String. Dlaczego to? Ciągi są typami wartości w Swift. Oznacza to, że jeśli użyjesz jednego ciągu, aby utworzyć nowy, należy go skopiować. Jest to dobre dla stabilności (nikt inny nie zmieni tego bez twojej wiedzy), ale złe dla wydajności.

Z drugiej strony podłańcuch jest odniesieniem do oryginalnego ciągu, z którego pochodzi. Oto obraz z dokumentacji ilustrujący to.

Kopiowanie nie jest potrzebne, więc jest znacznie wydajniejsze w użyciu. Wyobraź sobie jednak, że masz dziesięcioznakowy podłańcuch z milionowego ciągu znaków. Ponieważ podłańcuch odwołuje się do ciągu, system musiałby trzymać się całego ciągu tak długo, jak długo znajduje się on w pobliżu. Dlatego za każdym razem, gdy skończysz manipulować swoim podciągiem, przekonwertuj go na ciąg.

let myString = String(mySubstring)

Spowoduje to skopiowanie tylko podciągu, a pamięć zawierająca stary ciąg może zostać odzyskana . Podciągi (jako typ) mają być krótkotrwałe.

Kolejną dużą poprawą w Swift 4 jest to, że ciągi są kolekcjami (ponownie). Oznacza to, że cokolwiek możesz zrobić z kolekcją, możesz zrobić z łańcuchem (użyj indeksów dolnych, iteruj po znakach, filtruj itp.).

Poniższe przykłady pokazują, jak uzyskać podciąg w Swift.

Pobieranie podciągów

Można uzyskać podciąg z ciągu znaków za pomocą indeksów lub szereg innych metod (na przykład prefix, suffix, split). Nadal jednak musisz używać indeksu dla zakresu, String.Indexa nie Intindeksu. (Zobacz moją drugą odpowiedź, jeśli potrzebujesz pomocy.)

Początek łańcucha

Możesz użyć indeksu dolnego (zwróć uwagę na jednostronny zakres Swift 4):

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str[..<index] // Hello

lub prefix:

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str.prefix(upTo: index) // Hello

lub nawet łatwiej:

let mySubstring = str.prefix(5) // Hello

Koniec łańcucha

Korzystanie z indeksów dolnych:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str[index...] // playground

lub suffix:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str.suffix(from: index) // playground

lub nawet łatwiej:

let mySubstring = str.suffix(10) // playground

Zauważ, że podczas używania suffix(from: index)musiałem odliczać od końca, używając -10. Nie jest to konieczne przy zwykłym użyciu suffix(x), które po prostu pobiera ostatnie xznaki ciągu.

Zakres w ciągu

Ponownie używamy tutaj indeksów dolnych.

let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end

let mySubstring = str[range]  // play

Konwertowanie SubstringnaString

Nie zapominaj, że kiedy będziesz gotowy do zapisania swojego podciągu, powinieneś przekonwertować go na Stringtak, aby pamięć starego łańcucha mogła zostać wyczyszczona.

let myString = String(mySubstring)

Używasz Introzszerzenia indeksu?

Waham się przed użyciem Introzszerzenia indeksu opartego na indeksie po przeczytaniu artykułu Strings in Swift 3 autorstwa Airspeed Velocity i Ole Begemann. Chociaż w Swift 4 Strings są kolekcjami, zespół Swift celowo nie używał Intindeksów. Nadal jest String.Index. Ma to związek z tym, że znaki Swift składają się z różnej liczby punktów kodowych Unicode. Rzeczywisty indeks musi być jednoznacznie obliczony dla każdego łańcucha.

Muszę powiedzieć, że mam nadzieję, że zespół Swift znajdzie sposób na abstrahowanie String.Indexw przyszłości. Ale dopóki nie zdecyduję się użyć ich interfejsu API. Pomaga mi pamiętać, że manipulacje ciągami znaków to nie tylko proste Intwyszukiwanie indeksów.

Suragch
źródło
9
Dziękuję za opis. Zasłużone podwyżki. Apple to skomplikowało. Podciąg powinien być tak prosty, jak string.substring [od ... do].
Teddy
Naprawdę dobre wytłumaczenie. oprócz jednej drobnej rzeczy garbage collected;-) Mam nadzieję, że ludzie tutaj wiedzą, że w Swift nie ma śmieci.
Christian Anchor Dampf
@ChristianAnchorDampf, dzięki za poświęcenie czasu na komentarz. Wyjąłem śmieci. Jak brzmi nowe sformułowanie?
Suragch
Cóż za niesamowita odpowiedź!
davidev
194

Jestem naprawdę sfrustrowany modelem dostępu do ciągów Swift: wszystko musi być Index. Chcę tylko uzyskać dostęp do i-tego znaku ciągu Int, a nie niezdarny indeks i postęp (który zdarza się zmieniać z każdym większym wydaniem). Zrobiłem więc rozszerzenie do String:

extension String {
    func index(from: Int) -> Index {
        return self.index(startIndex, offsetBy: from)
    }

    func substring(from: Int) -> String {
        let fromIndex = index(from: from)
        return String(self[fromIndex...])
    }

    func substring(to: Int) -> String {
        let toIndex = index(from: to)
        return String(self[..<toIndex])
    }

    func substring(with r: Range<Int>) -> String {
        let startIndex = index(from: r.lowerBound)
        let endIndex = index(from: r.upperBound)
        return String(self[startIndex..<endIndex])
    }
}

let str = "Hello, playground"
print(str.substring(from: 7))         // playground
print(str.substring(to: 5))           // Hello
print(str.substring(with: 7..<11))    // play
Kod inny
źródło
5
Indeksy są bardzo przydatne, ponieważ znak może mieć więcej niż jeden bajt. Spróbujlet str = "🇨🇭🇩🇪🇺🇸Hello" print(str.substring(to: 2))
vadian
110
Tak, rozumiem, że znak (tj. Rozszerzony klaster grafemowy ) może zająć wiele bajtów. Moją frustracją jest to, że musimy korzystać z pełnej metody przesuwania indeksu, aby uzyskać dostęp do znaków ciągu. Dlaczego zespół Swift nie może po prostu dodać przeciążeń do Biblioteki podstawowej, aby ją wyodrębnić. Jeśli piszę str[5], chcę uzyskać dostęp do znaku o indeksie 5, niezależnie od tego, jak wygląda ten znak lub ile bajtów zajmuje. Czy Swift nie polega na produktywności programisty?
Kod Różny
6
@RenniePet Wierzę, że Apple rozpoznaje problem i nadchodzą zmiany. Zgodnie ze stroną Swift Evolution na GitHub: „Swift 4 dąży do tego, aby ciągi były bardziej wydajne i łatwiejsze w użyciu, zachowując domyślnie poprawność Unicode”. To jest niejasne, ale miejmy nadzieję
Kod Różny
3
@CodeDifferent, dlaczego jabłko nie dodało dostępu do indeksu dolnego? Aby ludzie zrozumieli, że to źle. Zasadniczo, jeśli zrobiłbyś dla i w 0..string.count używając indeksów, które byłyby podwójną pętlą, ponieważ pod maską indeks musi przejść przez każdy bajt łańcucha, aby dowiedzieć się, który jest następnym znakiem. Jeśli zapętlasz się za pomocą indeksu, iterujesz ciąg znaków tylko raz. Nawiasem mówiąc, sam tego nienawidzę, ale to jest powód, dla którego indeks dolny nie jest dostępny w łańcuchu w krótkim czasie.
Raimundas Sakalauskas
4
@RaimundasSakalauskas ten argument mnie nie omija. C # ma zarówno poprawność Unicode, jak i indeksowanie liczb całkowitych, co jest naprawdę wygodne. W Swift 1 Apple chciał, aby programiści używali go countElement(str)do znalezienia długości. W Swift 3 Apple stworzył ciąg niezgodny Sequencei zmusił wszystkich do użycia str.characters. Ci faceci nie boją się wprowadzać zmian. Ich upór w indeksowaniu liczb całkowitych jest naprawdę trudny do zrozumienia
Code Different
102

Rozszerzenie Swift 5:

extension String {
    subscript(_ range: CountableRange<Int>) -> String {
        let start = index(startIndex, offsetBy: max(0, range.lowerBound))
        let end = index(start, offsetBy: min(self.count - range.lowerBound, 
                                             range.upperBound - range.lowerBound))
        return String(self[start..<end])
    }

    subscript(_ range: CountablePartialRangeFrom<Int>) -> String {
        let start = index(startIndex, offsetBy: max(0, range.lowerBound))
         return String(self[start...])
    }
}

Stosowanie:

let s = "hello"
s[0..<3] // "hel"
s[3...]  // "lo"

Lub Unicode:

let s = "😎🤣😋"
s[0..<1] // "😎"
Lou Zell
źródło
2
O wiele lepiej, dziękuję za opublikowanie tego rozszerzenia! Myślę, że pochodzący z Pythona Swift jest o wiele trudniejszy niż to konieczne, aby się przyzwyczaić. Wydaje się, że dla osób idących w przeciwnym kierunku od Celu C do Szybkiego jest bardziej pozytywne potwierdzenie.
user3064009,
1
@Leon Właśnie go usunąłem. Przed 4.1 countbył dostępny tylko w dniuself.characters
Lou Zell,
1
Czy są jakieś braki, na które należy uważać dzięki temu konkretnemu rozszerzeniu? Dlaczego Apple nie zrobił czegoś takiego?
Andz
1
@Adzdz jest bardzo nieefektywny. Zaczyna się od początku ciągu - dwa razy - i musi parsować każdy znak stamtąd do „zasięgu” - dwa razy.
kareman
3
Musisz także dodać rozszerzenie, które zajmuje,CountableClosedRange<Int> jeśli chcesz napisać np s[0...2].
Chris Frederick
24

Swift 4 i 5:

extension String {
  subscript(_ i: Int) -> String {
    let idx1 = index(startIndex, offsetBy: i)
    let idx2 = index(idx1, offsetBy: 1)
    return String(self[idx1..<idx2])
  }

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

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

Jak tego użyć:

„abcde” [0] -> „a”

„abcde” [0 ... 2] -> „abc”

„abcde” [2 .. <4] -> „cd”

Souf ROCHDI
źródło
20

Szybki 4

W szybkim 4 Stringodpowiada Collection. Zamiast tego substringpowinniśmy teraz użyć subscript.Więc jeśli chcesz wyciąć tylko słowo "play"z "Hello, playground", możesz to zrobić w następujący sposób:

var str = "Hello, playground"
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let result = str[start..<end] // The result is of type Substring

Interesujące jest wiedzieć, że zrobienie tego da ci Substringzamiast String. Jest to szybkie i wydajne, ponieważ Substringdzieli pamięć z oryginalnym ciągiem. Jednak dzielenie pamięci w ten sposób może również łatwo doprowadzić do wycieków pamięci.

Dlatego powinieneś skopiować wynik do nowego ciągu, gdy chcesz wyczyścić oryginalny ciąg. Możesz to zrobić za pomocą normalnego konstruktora:

let newString = String(result)

Więcej informacji na temat nowej Substringklasy można znaleźć w [dokumentacji Apple]. 1

Jeśli na przykład otrzymasz Rangewynik w postaci NSRegularExpression, możesz użyć następującego rozszerzenia:

extension String {

    subscript(_ range: NSRange) -> String {
        let start = self.index(self.startIndex, offsetBy: range.lowerBound)
        let end = self.index(self.startIndex, offsetBy: range.upperBound)
        let subString = self[start..<end]
        return String(subString)
    }

}
gebirgsbärbel
źródło
Twój kod ulegnie awarii, jeśli range.upperBound ma> długość ciągu. Przydałoby się również przykładowe użycie, ponieważ nie znałem indeksów w Swift. Możesz dołączyć coś takiego jak datePartOnly = "2018-01-04-08: 00" [NSMakeRange (0, 10)]. Poza tym bardzo ładna odpowiedź, +1 :).
dcp
obecnie jest to dziwna rzecz: text[Range( nsRange , in: text)!]
Fattie
10

Oto funkcja, która zwraca podłańcuch danego podłańcucha, gdy podane są indeksy początkowy i końcowy. Aby uzyskać pełne odniesienie, możesz odwiedzić poniższe linki.

func substring(string: String, fromIndex: Int, toIndex: Int) -> String? {
    if fromIndex < toIndex && toIndex < string.count /*use string.characters.count for swift3*/{
        let startIndex = string.index(string.startIndex, offsetBy: fromIndex)
        let endIndex = string.index(string.startIndex, offsetBy: toIndex)
        return String(string[startIndex..<endIndex])
    }else{
        return nil
    }
}

Oto link do posta na blogu, który utworzyłem, aby szybko poradzić sobie z manipulacją ciągami. Manipulowanie strunami w trybie szybkim (obejmuje również swift 4)

Lub możesz zobaczyć tę istotę na github

Nikesh Jha
źródło
9

Miałem taką samą początkową reakcję. Ja również byłem sfrustrowany tym, że składnia i obiekty zmieniają się tak drastycznie w każdej większej wersji.

Jednak z doświadczenia zdałem sobie sprawę, że zawsze w końcu odczuwam konsekwencje walki z „zmianą”, np. Radzenia sobie z postaciami wielobajtowymi, co jest nieuniknione, jeśli spojrzysz na globalną publiczność.

Postanowiłem więc uznać i szanować wysiłki inżynierów Apple i wykonać swoją część, rozumiejąc ich sposób myślenia, kiedy wymyślili to „przerażające” podejście.

Zamiast tworzyć rozszerzenia, które są tylko obejściem, aby ułatwić Ci życie (nie mówię, że są złe lub drogie), dlaczego nie dowiedzieć się, w jaki sposób Struny są teraz zaprojektowane do pracy.

Na przykład miałem ten kod, który działał na Swift 2.2:

let rString = cString.substringToIndex(2)
let gString = (cString.substringFromIndex(2) as NSString).substringToIndex(2)
let bString = (cString.substringFromIndex(4) as NSString).substringToIndex(2)

i po rezygnacji z próby zastosowania tego samego podejścia, np. przy użyciu Substringów, w końcu zrozumiałem koncepcję traktowania Strings jako kolekcji dwukierunkowej, dla której skończyłem z tą wersją tego samego kodu:

let rString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let gString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let bString = String(cString.characters.prefix(2))

Mam nadzieję, że przyczyni się to ...

Rio Bautista
źródło
1
Cóż, radzenie sobie ze złożonym problemem nie oznacza, że ​​rozwiązanie może być eleganckie. Znów też rozumiem problem, ale cała klasa String i radzenie sobie z nim jest po prostu okropne.
słowa
5

Ta sama frustracja, to nie powinno być takie trudne ...

Skompilowałem ten przykład uzyskiwania pozycji dla podciągów z większego tekstu:

//
// Play with finding substrings returning an array of the non-unique words and positions in text
//
//

import UIKit

let Bigstring = "Why is it so hard to find substrings in Swift3"
let searchStrs : Array<String>? = ["Why", "substrings", "Swift3"]

FindSubString(inputStr: Bigstring, subStrings: searchStrs)


func FindSubString(inputStr : String, subStrings: Array<String>?) ->    Array<(String, Int, Int)> {
    var resultArray : Array<(String, Int, Int)> = []
    for i: Int in 0...(subStrings?.count)!-1 {
        if inputStr.contains((subStrings?[i])!) {
            let range: Range<String.Index> = inputStr.range(of: subStrings![i])!
            let lPos = inputStr.distance(from: inputStr.startIndex, to: range.lowerBound)
            let uPos = inputStr.distance(from: inputStr.startIndex, to: range.upperBound)
            let element = ((subStrings?[i])! as String, lPos, uPos)
            resultArray.append(element)
        }
    }
    for words in resultArray {
        print(words)
    }
    return resultArray
}

zwraca („Dlaczego”, 0, 3) („podłańcuchy”, 26, 36) („Swift3”, 40, 46)

Tall Dane
źródło
3
To jest trochę kodu, ale tak naprawdę nie wyjaśnia, jak indeksowanie łańcuchów i podciągi działają w swift3.
Robert,
5

Jestem nowy w Swift 3, ale patrząc na Stringskładnię (indeks) dla analogii, myślę, że indeks jest jak „wskaźnik” ograniczony do łańcucha, a Int może pomóc jako niezależny obiekt. Używając składni base + offset, możemy uzyskać i-ty znak z ciągu znaków z poniższym kodem:

let s = "abcdefghi"
let i = 2
print (s[s.index(s.startIndex, offsetBy:i)])
// print c

Dla zakresu znaków (indeksów) od łańcucha przy użyciu składni String (zakres) możemy uzyskać od i-tego do f-tego znaku z poniższym kodem:

let f = 6
print (s[s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 )])
//print cdefg

W przypadku podłańcucha (zakresu) z łańcucha przy użyciu String.substring (range) możemy uzyskać podłańcuch za pomocą poniższego kodu:

print (s.substring (with:s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 ) ) )
//print cdefg

Uwagi:

  1. I-ta i-ta zaczynają się od 0.

  2. Do f-tej używam offsetBY: f + 1, ponieważ zakres abonamentu wykorzystuje .. <(operator półotwarty), nie obejmuje piątej pozycji.

  3. Oczywiście musi zawierać błędy sprawdzania poprawności, takie jak nieprawidłowy indeks.

Nelson Mizutani
źródło
5

Swift 4+

extension String {
    func take(_ n: Int) -> String {
        guard n >= 0 else {
            fatalError("n should never negative")
        }
        let index = self.index(self.startIndex, offsetBy: min(n, self.count))
        return String(self[..<index])
    }
}

Zwraca podsekwencję pierwszych n znaków lub całego łańcucha, jeśli łańcuch jest krótszy. (inspirowany: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/take.html )

Przykład:

let text = "Hello, World!"
let substring = text.take(5) //Hello
Peter Kreinz
źródło
4

Mam dość mechaniczne myślenie. Oto podstawy ...

Swift 4 Swift 5

  let t = "abracadabra"

  let start1 = t.index(t.startIndex, offsetBy:0)
  let   end1 = t.index(t.endIndex, offsetBy:-5)
  let start2 = t.index(t.endIndex, offsetBy:-5)
  let   end2 = t.index(t.endIndex, offsetBy:0)

  let t2 = t[start1 ..< end1]
  let t3 = t[start2 ..< end2]                

  //or a shorter form 

  let t4 = t[..<end1]
  let t5 = t[start2...]

  print("\(t2) \(t3) \(t)")
  print("\(t4) \(t5) \(t)")

  // result:
  // abraca dabra abracadabra

Wynikiem jest podłańcuch, co oznacza, że ​​jest on częścią oryginalnego łańcucha. Aby uzyskać pełny ciąg oddzielnych ciągów, wystarczy użyć np

    String(t3)
    String(t4)

Oto, czego używam:

    let mid = t.index(t.endIndex, offsetBy:-5)
    let firstHalf = t[..<mid]
    let secondHalf = t[mid...]
t1ser
źródło
3

Szybki 4

extension String {
    subscript(_ i: Int) -> String {
        let idx1 = index(startIndex, offsetBy: i)
        let idx2 = index(idx1, offsetBy: 1)
        return String(self[idx1..<idx2])
    }
}

let s = "hello"

s[0]    // h
s[1]    // e
s[2]    // l
s[3]    // l
s[4]    // o
FlowUI. SimpleUITesting.com
źródło
2

Stworzyłem do tego proste rozszerzenie (Swift 3)

extension String {
    func substring(location: Int, length: Int) -> String? {
        guard characters.count >= location + length else { return nil }
        let start = index(startIndex, offsetBy: location)
        let end = index(startIndex, offsetBy: location + length)
        return substring(with: start..<end)
    }
}
Lucas Algarra
źródło
2

Oto bardziej ogólna implementacja:

Ta technika wciąż stosuje się indexdo standardów Swift i sugeruje pełną Postać.

extension String
{
    func subString <R> (_ range: R) -> String? where R : RangeExpression, String.Index == R.Bound
    {
        return String(self[range])
    }

    func index(at: Int) -> Index
    {
        return self.index(self.startIndex, offsetBy: at)
    }
}

Aby podciąć ciąg od trzeciego znaku:

let item = "Fred looks funny"
item.subString(item.index(at: 2)...) // "ed looks funny"

Użyłem wielbłąda, subStringaby wskazać, że zwraca a, Stringa nie a Substring.

Leslie Godwin
źródło
2

Opierając się na powyższym, musiałem podzielić ciąg znaków na znak niedrukowalny, porzucając znak niedrukowalny. Opracowałem dwie metody:

var str = "abc\u{1A}12345sdf"
let range1: Range<String.Index> = str.range(of: "\u{1A}")!
let index1: Int = str.distance(from: str.startIndex, to: range1.lowerBound)
let start = str.index(str.startIndex, offsetBy: index1)
let end = str.index(str.endIndex, offsetBy: -0)
let result = str[start..<end] // The result is of type Substring
let firstStr = str[str.startIndex..<range1.lowerBound]

które zestawiłem, używając niektórych z powyższych odpowiedzi.

Ponieważ ciąg jest kolekcją, wykonałem następujące czynności:

var fString = String()
for (n,c) in str.enumerated(){

*if c == "\u{1A}" {
    print(fString);
    let lString = str.dropFirst(n + 1)
    print(lString)
    break
   }
 fString += String(c)
}*

Co było dla mnie bardziej intuicyjne. Który jest najlepszy? Nie mogę powiedzieć, że oboje pracują z Swift 5

Jeremy Andrews
źródło
Dzięki za odpowiedź. Czy jest coś innego w Strings w Swift 5? Nie miałem jeszcze czasu na zabawę.
Suragch
Mówią tak, ale nie miałem okazji się temu przyjrzeć.
Jeremy Andrews,
1

Szybki 4

„Podciąg” ( https://developer.apple.com/documentation/swift/substring ):

let greeting = "Hi there! It's nice to meet you! 👋"
let endOfSentence = greeting.index(of: "!")!
let firstSentence = greeting[...endOfSentence]
// firstSentence == "Hi there!"

Przykład rozszerzenia String:

private typealias HowDoYouLikeThatElonMusk = String
private extension HowDoYouLikeThatElonMusk {

    subscript(_ from: Character?, _ to: Character?, _ include: Bool) -> String? {
        if let _from: Character = from, let _to: Character = to {
            let dynamicSourceForEnd: String = (_from == _to ? String(self.reversed()) : self)
            guard let startOfSentence: String.Index = self.index(of: _from),
                let endOfSentence: String.Index = dynamicSourceForEnd.index(of: _to) else {
                return nil
            }

            let result: String = String(self[startOfSentence...endOfSentence])
            if include == false {
                guard result.count > 2 else {
                        return nil
                }
                return String(result[result.index(result.startIndex, offsetBy: 1)..<result.index(result.endIndex, offsetBy: -1)])
            }
            return result
        } else if let _from: Character = from {
            guard let startOfSentence: String.Index = self.index(of: _from) else {
                return nil
            }
            let result: String = String(self[startOfSentence...])
            if include == false {
                guard result.count > 1 else {
                    return nil
                }
                return String(result[result.index(result.startIndex, offsetBy: 1)...])
            }
            return result
        } else if let _to: Character = to {
            guard let endOfSentence: String.Index = self.index(of: _to) else {
                    return nil
            }
            let result: String = String(self[...endOfSentence])
            if include == false {
                guard result.count > 1 else {
                    return nil
                }
                return String(result[..<result.index(result.endIndex, offsetBy: -1)])
            }
            return result
        }
        return nil
    }
}

przykład użycia rozszerzenia String:

let source =                                   ">>>01234..56789<<<"
// include = true
var from =          source["3", nil, true]  //       "34..56789<<<"
var to =            source[nil, "6", true]  // ">>>01234..56"
var fromTo =        source["3", "6", true]  //       "34..56"
let notFound =      source["a", nil, true]  // nil
// include = false
from =              source["3", nil, false] //        "4..56789<<<"
to =                source[nil, "6", false] // ">>>01234..5"
fromTo =            source["3", "6", false] //        "4..5"
let outOfBounds =   source[".", ".", false] // nil

let str = "Hello, playground"
let hello = str[nil, ",", false] // "Hello"
CAHbl463
źródło
-1

Swift 5
let desiredIndex: Int = 7 let substring = str[String.Index(encodedOffset: desiredIndex)...]
Ta zmienna podła daje wynik.
Po prostu tutaj Int jest konwertowany na indeks, a następnie możesz podzielić ciągi. Chyba że otrzymasz błędy.

Wimukthi Rajapaksha
źródło
2
To jest źle. Postać może składać się z jednego lub więcej bajtów. Działa tylko z tekstem ascii.
Leo Dabus