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 String
ma 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.
Odpowiedzi:
Zastosowano wszystkie poniższe przykłady
Szybki 4
Ciągi zostały dość gruntownie zmienione w Swift 4. Kiedy teraz otrzymujesz podciąg z String, dostajesz z
Substring
powrotem typ zamiast aString
. 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.
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.Index
a nieInt
indeksu. (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):
lub
prefix
:lub nawet łatwiej:
Koniec łańcucha
Korzystanie z indeksów dolnych:
lub
suffix
:lub nawet łatwiej:
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życiusuffix(x)
, które po prostu pobiera ostatniex
znaki ciągu.Zakres w ciągu
Ponownie używamy tutaj indeksów dolnych.
Konwertowanie
Substring
naString
Nie zapominaj, że kiedy będziesz gotowy do zapisania swojego podciągu, powinieneś przekonwertować go na
String
tak, aby pamięć starego łańcucha mogła zostać wyczyszczona.Używasz
Int
rozszerzenia indeksu?Waham się przed użyciem
Int
rozszerzenia 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łInt
indeksów. Nadal jestString.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.Index
w 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 prosteInt
wyszukiwanie indeksów.źródło
garbage collected
;-) Mam nadzieję, że ludzie tutaj wiedzą, że w Swift nie ma śmieci.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ąguInt
, a nie niezdarny indeks i postęp (który zdarza się zmieniać z każdym większym wydaniem). Zrobiłem więc rozszerzenie doString
:źródło
let str = "🇨🇭🇩🇪🇺🇸Hello"
print(str.substring(to: 2))
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?countElement(str)
do znalezienia długości. W Swift 3 Apple stworzył ciąg niezgodnySequence
i zmusił wszystkich do użyciastr.characters
. Ci faceci nie boją się wprowadzać zmian. Ich upór w indeksowaniu liczb całkowitych jest naprawdę trudny do zrozumieniaRozszerzenie Swift 5:
Stosowanie:
Lub Unicode:
źródło
count
był dostępny tylko w dniuself.characters
CountableClosedRange<Int>
jeśli chcesz napisać nps[0...2]
.Swift 4 i 5:
Jak tego użyć:
źródło
Szybki 4
W szybkim 4
String
odpowiadaCollection
. Zamiast tegosubstring
powinniś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:Interesujące jest wiedzieć, że zrobienie tego da ci
Substring
zamiastString
. Jest to szybkie i wydajne, ponieważSubstring
dzieli 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:
Więcej informacji na temat nowej
Substring
klasy można znaleźć w [dokumentacji Apple]. 1Jeśli na przykład otrzymasz
Range
wynik w postaciNSRegularExpression
, możesz użyć następującego rozszerzenia:źródło
text[Range( nsRange , in: text)!]
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.
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
źródło
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:
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:
Mam nadzieję, że przyczyni się to ...
źródło
Ta sama frustracja, to nie powinno być takie trudne ...
Skompilowałem ten przykład uzyskiwania pozycji dla podciągów z większego tekstu:
zwraca („Dlaczego”, 0, 3) („podłańcuchy”, 26, 36) („Swift3”, 40, 46)
źródło
Jestem nowy w Swift 3, ale patrząc na
String
skł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: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:
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:
Uwagi:
I-ta i-ta zaczynają się od 0.
Do f-tej używam offsetBY: f + 1, ponieważ zakres abonamentu wykorzystuje .. <(operator półotwarty), nie obejmuje piątej pozycji.
Oczywiście musi zawierać błędy sprawdzania poprawności, takie jak nieprawidłowy indeks.
źródło
Swift 4+
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:
źródło
Mam dość mechaniczne myślenie. Oto podstawy ...
Swift 4 Swift 5
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
Oto, czego używam:
źródło
Szybki 4
źródło
Stworzyłem do tego proste rozszerzenie (Swift 3)
źródło
Oto bardziej ogólna implementacja:
Ta technika wciąż stosuje się
index
do standardów Swift i sugeruje pełną Postać.Aby podciąć ciąg od trzeciego znaku:
Użyłem wielbłąda,
subString
aby wskazać, że zwraca a,String
a nie aSubstring
.źródło
Opierając się na powyższym, musiałem podzielić ciąg znaków na znak niedrukowalny, porzucając znak niedrukowalny. Opracowałem dwie metody:
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:
Co było dla mnie bardziej intuicyjne. Który jest najlepszy? Nie mogę powiedzieć, że oboje pracują z Swift 5
źródło
Szybki 4
„Podciąg” ( https://developer.apple.com/documentation/swift/substring ):
Przykład rozszerzenia String:
przykład użycia rozszerzenia String:
źródło
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.
źródło