Jak korzystać z String.substringWithRange? (lub jak działają zakresy w Swift?)

266

Nie byłem jeszcze w stanie dowiedzieć się, jak uzyskać podciąg Stringw Swift:

var str =Hello, playground”
func test(str: String) -> String {
 return str.substringWithRange( /* What goes here? */ )
}
test (str)

Nie mogę utworzyć zasięgu w Swift. Autouzupełnianie na placu zabaw nie jest super pomocne - sugeruje to:

return str.substringWithRange(aRange: Range<String.Index>)

W Swift Standard Reference Library nie znalazłem nic, co by pomogło. Oto kolejne dzikie przypuszczenie:

return str.substringWithRange(Range(0, 1))

I to:

let r:Range<String.Index> = Range<String.Index>(start: 0, end: 2)
return str.substringWithRange(r)

Widziałem inne odpowiedzi ( Finding index of character in Swift String ), które wydają się sugerować, że skoro Stringjest to rodzaj mostu NSString, „stare” metody powinny działać, ale nie jest jasne, jak - np. To też nie działa ( nie wydaje się być poprawną składnią):

let x = str.substringWithRange(NSMakeRange(0, 3))

Myśli?

Obrabować
źródło
Co masz na myśli mówiąc, że ostatni przykład nie działa? Czy powoduje błąd lub zwraca nieoczekiwaną wartość?
67cherries
Proszę dupe rdar: // 17158813 z prośbą o zapisanie indeksu dolnego openradar.appspot.com/radar?id=6373877630369792
Rob Napier

Odpowiedzi:

259

Możesz użyć metody substringWithRange. Zajmuje początek i koniec String.Index.

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex, end: str.endIndex)) //"Hello, playground"

Aby zmienić indeks początkowy i końcowy, użyj AdvancedBy (n).

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex.advancedBy(2), end: str.endIndex.advancedBy(-1))) //"llo, playgroun"

Nadal możesz również używać metody NSString z NSRange, ale musisz się upewnić, że używasz NSString w następujący sposób:

let myNSString = str as NSString
myNSString.substringWithRange(NSRange(location: 0, length: 3))

Uwaga: jak wspomniano JanX2, ta druga metoda nie jest bezpieczna w przypadku ciągów znaków Unicode.

Connor
źródło
5
Tak. Na razie w razie potrzeby kontynuowałbym połączenie z NSString. Wygląda na to, że Swift's String jest wciąż niekompletny
Connor
14
To nie jest bezpieczne! Zakładasz, że indeksy na String i NSString są wymienne. Tak nie jest. Indeksy w ciągu odnoszą się do punktów kodu Unicode, podczas gdy indeksy w ciągu NSString odnoszą się do jednostek kodu UTF-16! Wypróbuj z emoji.
JanX2
3
Należy pamiętać, że Indeksy w ciągu NIE odnoszą się już do punktów kodu Unicode. Pomyśl o nich jako odnoszących się do znaków postrzeganych przez użytkownika.
JanX2,
2
Jaka jest więc właściwa odpowiedź? Dzwonienie advanceoznacza, że ​​musimy iterować całą drogę przez możliwie długi ciąg. Ale tworzenie NSRange i jawne używanie NSString jest niebezpieczne? Co zostało?
mat
1
Dzięki, m8. Przejście na Swift jest dość nieuporządkowane po 4 latach wykonywania Celu-C.
Felipe,
162

Swift 2

Prosty

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

Szybki 3

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)

str[startIndex...endIndex]       // "Strin"
str.substring(to: startIndex)    // "My "
str.substring(from: startIndex)  // "String"

Szybki 4

substring(to:)i substring(from:)są przestarzałe w Swift 4.

String(str[..<startIndex])    // "My "
String(str[startIndex...])    // "String"
String(str[startIndex...endIndex])    // "Strin"
Warif Akhand Rishi
źródło
2
TO. Tak mylące, że musisz znaleźć swój indeks, zanim będziesz mógł go użyć. Nie mam pojęcia, dlaczego zajęło mi to tak długo, ale dziękuję za twój wkład w ludzkość.
Warpzit
1
W szybkich 4 operacjach podłańcuchowych zwraca instancję typu Podłańcuch zamiast Łańcucha. Konieczne jest przekonwertowanie wyniku na ciąg znaków w celu długoterminowego przechowywania. let newString = String (substring)
phnmnn
43

UWAGA: @airspeedswift przedstawia kilka bardzo wnikliwych punktów na temat kompromisów tego podejścia, szczególnie ukrytych wpływów na wydajność. Łańcuchy nie są prostymi bestiami, a dotarcie do określonego indeksu może zająć czas O (n), co oznacza, że ​​pętla korzystająca z indeksu dolnego może być O (n ^ 2). Zostałeś ostrzeżony.

Musisz tylko dodać nową subscriptfunkcję, która ma zasięg i używa, advancedBy()aby przejść do żądanego miejsca:

import Foundation

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let startIndex = self.startIndex.advancedBy(r.startIndex)
            let endIndex = startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

var s = "Hello, playground"

println(s[0...5]) // ==> "Hello,"
println(s[0..<5]) // ==> "Hello"

(To zdecydowanie powinno być częścią języka. Proszę o dupe rdar: // 17158813 )

Dla zabawy możesz także dodać +operatora do indeksów:

func +<T: ForwardIndex>(var index: T, var count: Int) -> T {
  for (; count > 0; --count) {
    index = index.succ()
  }
  return index
}

s.substringWithRange(s.startIndex+2 .. s.startIndex+5)

(Nie wiem jeszcze, czy ten powinien być częścią języka, czy nie.)

Rob Napier
źródło
2
Prawdopodobnie powinniśmy pracować z String.Index zamiast indeksów liczb całkowitych w jak największym stopniu ze względu na wydajność. Każda konwersja z indeksu liczb całkowitych na String.Index musi przetworzyć ciąg, aby wyszukać.
JanX2
11
@AndrewDunn To nie jest przedwczesna optymalizacja. Każda konwersja z indeksu liczb całkowitych na String.Index ma wartość O (n), a nie O (1)! Przeczytaj komentarze do advance<T>(…)w Swiftprzestrzeni nazw.
JanX2,
1
Cała implementacja String odbywa się za pomocą String.Index. W tym przypadku nie tyle optymalizacja, co utrzymanie wszystkiego w prostocie. Konieczność żonglowania między int i String.Index wszędzie tam iz powrotem doprowadzi do bałaganu.
chakrit
@chakrit Tylko jeśli indeksy, których musisz użyć, nie są już liczbami całkowitymi (zamiast być String.Indexes), co często ma miejsce. Niż możesz sobie życzyć, aby wszystkie te metody obsługi ciągów pracowały z Ints. I jesteś zmuszony przekonwertować to, String.Indexesco prowadzi do bałaganu, o którym wspomniałeś.
Rasto,
Zainteresowany czytelnik powinien rzucić okiem na ExSwift github.com/pNre/ExSwift
AsTeR
42

W chwili pisania tego artykułu żadne rozszerzenie nie jest w pełni kompatybilne z Swift 4.2 , więc oto jedno, które obejmuje wszystkie potrzeby, o których mogłem pomyśleć:

extension String {
    func substring(from: Int?, to: Int?) -> String {
        if let start = from {
            guard start < self.count else {
                return ""
            }
        }

        if let end = to {
            guard end >= 0 else {
                return ""
            }
        }

        if let start = from, let end = to {
            guard end - start >= 0 else {
                return ""
            }
        }

        let startIndex: String.Index
        if let start = from, start >= 0 {
            startIndex = self.index(self.startIndex, offsetBy: start)
        } else {
            startIndex = self.startIndex
        }

        let endIndex: String.Index
        if let end = to, end >= 0, end < self.count {
            endIndex = self.index(self.startIndex, offsetBy: end + 1)
        } else {
            endIndex = self.endIndex
        }

        return String(self[startIndex ..< endIndex])
    }

    func substring(from: Int) -> String {
        return self.substring(from: from, to: nil)
    }

    func substring(to: Int) -> String {
        return self.substring(from: nil, to: to)
    }

    func substring(from: Int?, length: Int) -> String {
        guard length > 0 else {
            return ""
        }

        let end: Int
        if let start = from, start > 0 {
            end = start + length - 1
        } else {
            end = length - 1
        }

        return self.substring(from: from, to: end)
    }

    func substring(length: Int, to: Int?) -> String {
        guard let end = to, end > 0, length > 0 else {
            return ""
        }

        let start: Int
        if let end = to, end - length > 0 {
            start = end - length + 1
        } else {
            start = 0
        }

        return self.substring(from: start, to: to)
    }
}

Następnie możesz użyć:

let string = "Hello,World!"

string.substring(from: 1, to: 7)dostaje: ello,Wo

string.substring(to: 7)dostaje: Hello,Wo

string.substring(from: 3)dostaje: lo,World!

string.substring(from: 1, length: 4)dostaje: ello

string.substring(length: 4, to: 7)dostaje: o,Wo

Zaktualizowano substring(from: Int?, length: Int)do obsługi począwszy od zera.

zimowany
źródło
Jak to obsługuje emoji i rozszerzone klastry grafemowe? (Powinienem to przetestować sam, ale w tej chwili nie jest to wygodne.)
Suragch,
2
@Suragch Poradzi sobie OK: "Hello😇😍😘😎📸📀💾∝ℵℶ≹".substring(from: 4, to: 14)daje nam:o😇😍😘😎📸📀💾∝ℵℶ
zimowany
Wyglądają jednak jak normalne postacie. Spróbuj użyć emoji takiego jak 👩‍👩‍👧‍👦 lub 🇨🇦🇺🇸🇲🇽
Supuhstar 21.04.17
31

SWIFT 2.0

prosty:

let myString = "full text container"
let substring = myString[myString.startIndex..<myString.startIndex.advancedBy(3)] // prints: ful

SWIFT 3.0

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

SWIFT 4.0

Operacje na ciągach znaków zwracają instancję typu Podłańcuch zamiast Ciąg.

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

// Convert the result to a String for long-term storage.
let newString = String(substring)
phnmnn
źródło
4
Niestety, nie jest już kompatybilny z Swift 3 od Xcode 8 beta 6
Ben Morrow,
1
Pracuje dla mnie. Xcode 8, Swift 3. Brak wersji beta w systemie Mac OS Sierra. text[text.startIndex..<text.index(text.startIndex, offsetBy: 2)]
Jose Ramirez
18

Jest to o wiele prostsze niż dowolna z odpowiedzi tutaj, gdy znajdziesz właściwą składnię.

Chcę zabrać [i]

let myString = "[ABCDEFGHI]"
let startIndex = advance(myString.startIndex, 1) //advance as much as you like
let endIndex = advance(myString.endIndex, -1)
let range = startIndex..<endIndex
let myNewString = myString.substringWithRange( range )

wynikiem będzie „ABCDEFGHI”, można również użyć parametru startIndex i endIndex

let mySubString = myString.substringFromIndex(startIndex)

i tak dalej!

PS: Jak wskazano w uwagach, w swift 2, które są dostarczane z Xcode 7 i iOS9, wprowadzono pewne zmiany składniowe!

Proszę spojrzeć na tę stronę

użytkownik1700737
źródło
Tak, miałem te same problemy z tym, że debugger nie wyświetlał poprawnego ciągu kilka razy?
user1700737,
Mam ciąg taki jak ten / Data (147410000000) /. Jak mogę uzyskać wartość zamiast określenia indeksu. Potrzebuję uzyskać wartość za pomocą rangeOfString („(”) i rangeOfString („)”). Wynik powinien wynosić 147410000000. Hoe, mogę to zdobyć
iPhone Guy
To nie jest takie trudne: var str = "/ Date (147410000000) /." var range = str.rangeOfString ("(")!! endIndex .. <str.rangeOfString (")") !. startIndex niech myNewString = str.substringWithRange (zakres)
użytkownik1700737
4
od advance(myString.endIndex, -1)czasu zmiany składni Xcode 7 beta 6 namyString.endIndex.advancedBy(-1)
l --marc l
16

Na przykład, aby znaleźć imię (do pierwszej spacji) w moim pełnym imieniu:

let name = "Joris Kluivers"

let start = name.startIndex
let end = find(name, " ")

if end {
    let firstName = name[start..end!]
} else {
    // no space found
}

starti endString.Indextutaj typu i są używane do utworzenia Range<String.Index>i używane w akcesorium do indeksu dolnego (jeśli w oryginalnym ciągu znajduje się spacja).

Trudno jest utworzyć String.Indexbezpośrednio z pozycji liczby całkowitej stosowanej w poście otwierającym. Wynika to z faktu, że w moim imieniu każdy znak miałby jednakowy rozmiar w bajtach. Ale znaki używające specjalnych akcentów w innych językach mogły użyć jeszcze kilku bajtów (w zależności od zastosowanego kodowania). Do jakiego bajtu powinna się odnosić liczba całkowita?

Możliwe jest utworzenie nowego String.Index od istniejącego przy użyciu metod succi predktóry zadba o właściwą liczbę bajtów są pomijane, aby dostać się do następnego punktu kodu w kodowaniu. Jednak w tym przypadku łatwiej jest wyszukać indeks pierwszej spacji w ciągu, aby znaleźć indeks końcowy.

Joris Kluivers
źródło
16

Ponieważ String jest typem mostka dla NSString, „stare” metody powinny działać, ale nie jest jasne, w jaki sposób - np. To również nie działa (nie wydaje się być poprawną składnią):

 let x = str.substringWithRange(NSMakeRange(0, 3))

Dla mnie to naprawdę interesująca część twojego pytania. String jest zmostkowane do NSString, więc większość metody NSString zrobić pracę bezpośrednio na sznurku. Możesz z nich korzystać swobodnie i bez zastanowienia. Na przykład działa to tak, jak się spodziewasz:

// delete all spaces from Swift String stateName
stateName = stateName.stringByReplacingOccurrencesOfString(" ", withString:"")

Ale, jak to często bywa, „mam moje mojo, ale po prostu nie działa na ciebie”. Właśnie zdarzyło Ci się wybrać jeden z rzadkich przypadków, w których istnieje równoległa metoda Swift o identycznej nazwie , aw takim przypadku metoda Swift przesłania metodę Objective-C. Zatem, kiedy mówisz str.substringWithRange, Swift myśli, że masz na myśli metodę Swift, a nie metodę NSString - i wtedy jesteś znienawidzony, ponieważ metoda Swift oczekujeRange<String.Index> , a nie wiesz, jak zrobić jedną z nich.

Najłatwiejszym sposobem jest powstrzymanie Swifta przed przesłaniem w ten sposób, przesyłając wprost:

let x = (str as NSString).substringWithRange(NSMakeRange(0, 3))

Pamiętaj, że nie wymaga to żadnych znaczących dodatkowych prac. „Obsada” nie oznacza „konwersji”; ciąg jest efektywnie ciągiem NSS. Mówimy tylko Swiftowi, jak patrzeć na tę zmienną na potrzeby tego jednego wiersza kodu.

Naprawdę dziwną częścią tego wszystkiego jest to, że metoda Szybka, która powoduje wszystkie te kłopoty, jest nieudokumentowana. Nie mam pojęcia, gdzie to jest zdefiniowane; nie ma go w nagłówku NSString i nie ma go również w nagłówku Swift.

matowy
źródło
Sugeruję jednak zgłoszenie błędu. Jabłko powinno „łowić ryby lub kroić przynętę” - albo dać nam sposób na zbudowanie Range<String.Index>, albo usunąć tę nieudokumentowaną metodę z naszej strony.
mat
Cholera - jest to przydatne. Pomimo tego, co mówi dokumentacja, nie zrozumiałem, co poszło źle, dopóki tego nie przeczytałem. Teraz posortowane :)
Jon M
13

W nowym użyciu Xcode 7.0

//: Playground - noun: a place where people can play

import UIKit

var name = "How do you use String.substringWithRange?"
let range = name.startIndex.advancedBy(0)..<name.startIndex.advancedBy(10)
name.substringWithRange(range)

//OUT:

wprowadź opis zdjęcia tutaj

Claudio Guirunas
źródło
2
.advancedBy(0) nie jest konieczne dla startIndex.
Mat0
12

Krótka odpowiedź brzmi: w Swift jest to naprawdę trudne. Mam przeczucie, że Apple wciąż ma wiele pracy nad metodami wygody dla takich rzeczy.

String.substringWithRange()oczekuje Range<String.Index>parametru i, o ile mogę stwierdzić, nie ma metody generatora dla tego String.Indextypu. Można uzyskać String.Indexwartości z powrotem z aString.startIndexi aString.endIndexi zadzwoń .succ()lub.pred() na nich, ale to szaleństwo.

Co powiesz na rozszerzenie klasy String, które zajmuje dobre stare Ints?

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let subStart = advance(self.startIndex, r.startIndex, self.endIndex)
            let subEnd = advance(subStart, r.endIndex - r.startIndex, self.endIndex)
            return self.substringWithRange(Range(start: subStart, end: subEnd))
        }
    }
    func substring(from: Int) -> String {
        let end = countElements(self)
        return self[from..<end]
    }
    func substring(from: Int, length: Int) -> String {
        let end = from + length
        return self[from..<end]
    }
}

let mobyDick = "Call me Ishmael."
println(mobyDick[8...14])             // Ishmael

let dogString = "This 🐶's name is Patch."
println(dogString[5..<6])               // 🐶
println(dogString[5...5])              // 🐶
println(dogString.substring(5))        // 🐶's name is Patch.
println(dogString.substring(5, length: 1))   // 🐶

Aktualizacja: Swift beta 4 rozwiązuje poniższe problemy!

W obecnej wersji [w wersji beta 3 i wcześniejszych] nawet ciągi natywne Swift mają pewne problemy z obsługą znaków Unicode. Powyższa ikona psa działała, ale następujące nie:

let harderString = "1:1️⃣"
for character in harderString {
    println(character)
}

Wynik:

1
:
1
️
⃣
Nate Cook
źródło
1
To dziwne, podstawowe klasy Swift wydają się po prostu pozbawione wielu podstawowych funkcji. Nie sądzę, że Apple chce, abyśmy stale odwoływali się do klas Foundation w celu wykonywania prostych zadań.
bluedevil2k
1
Całkowicie. Należy pamiętać, że Swift został zasadniczo opracowany w ciemności i każdy ma inną definicję „prostych zadań” - założę się, że widzimy szybką iterację na te tematy.
Nate Cook
@ JanX2 - Dzięki, masz całkowitą rację. Bezproblemowe łączenie z NSStringzmusiło mnie do myślenia, że ​​korzystam tylko z Stringmetod rodzimych Swift , ponieważ nie używałem żadnych myString as NSStringwywołań.
Nate Cook
W tej chwili jest też kilka błędów w Unicode w obsłudze napisów Swift: zobacz moją notatkę na końcu.
Nate Cook
@NateCook Czy możesz złożyć radar dotyczący tego problemu?
JanX2
11

Możesz użyć tego rozszerzenia do ulepszenia substringWithRange

Swift 2.3

extension String
{   
    func substringWithRange(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(end))
        return self.substringWithRange(range)
    }

    func substringWithRange(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(start + location))
        return self.substringWithRange(range)
    }
}

Szybki 3

extension String
{
    func substring(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: end)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }

    func substring(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: start + location)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }
}

Stosowanie:

let str = "Hello, playground"

let substring1 = str.substringWithRange(0, end: 5) //Hello
let substring2 = str.substringWithRange(7, location: 10) //playground
ChikabuZ
źródło
7

Przykładowy kod, jak uzyskać podciąg w Swift 2.0

(i) Podciąg z indeksu początkowego

Wejście:-

var str = "Swift is very powerful language!"
print(str)

str = str.substringToIndex(str.startIndex.advancedBy(5))
print(str)

Wynik:-

Swift is very powerful language!
Swift

(ii) Podciąg z określonego indeksu

Wejście:-

var str = "Swift is very powerful language!"
print(str)

str = str.substringFromIndex(str.startIndex.advancedBy(6)).substringToIndex(str.startIndex.advancedBy(2))
print(str)

Wynik:-

Swift is very powerful language!
is

Mam nadzieję, że ci to pomoże!

Umang Kothari
źródło
7

Proste rozwiązanie z niewielkim kodem.

Stwórz rozszerzenie, które obejmuje podstawowe subString, które mają prawie wszystkie inne języki:

extension String {
    func subString(start: Int, end: Int) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: start)
        let endIndex = self.index(startIndex, offsetBy: end)

        let finalString = self.substring(from: startIndex)
        return finalString.substring(to: endIndex)
    }
}

Po prostu zadzwoń za pomocą

someString.subString(start: 0, end: 6)
Oliver Dixon
źródło
1
Parametr „koniec” w rzeczywistości oznacza tutaj długość, więc myślę, że należy na to zmienić.
Jovan Jovanovski
Myślę, że używanie długości jest dość mylące. W rzeczywistości jest to „koniec” miejsca, w którym chcesz, aby subString był, a nie faktyczna długość wyniku subString.
Oliver Dixon
6

To działa na moim placu zabaw :)

String(seq: Array(str)[2...4])
patrykens
źródło
W rzeczywistości jest to bardziej wydajne niż dzwonienieadvance
Erik Aigner
2
Nie działa na moim placu zabaw Xcode 7 (Swift 2), ale działa ... 'var str = "Hello, plac zabaw"' String (Array (str.characters) [7]) // "p" String (Array ( str. znaków) [7 ... 10]) // „play” String (Array (str. znaków) [7 .. <10]) // „pla”
Vince O'Sullivan
6

Zaktualizowano dla Xcode 7. Dodaje rozszerzenie String:

Posługiwać się:

var chuck: String = "Hello Chuck Norris"
chuck[6...11] // => Chuck

Realizacja:

extension String {

    /**
     Subscript to allow for quick String substrings ["Hello"][0...1] = "He"
     */
    subscript (r: Range<Int>) -> String {
        get {
            let start = self.startIndex.advancedBy(r.startIndex)
            let end = self.startIndex.advancedBy(r.endIndex - 1)
            return self.substringWithRange(start..<end)
        }
    }

}
Firo
źródło
4

spróbuj tego na placu zabaw

var str:String = "Hello, playground"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,8))

da ci „ello, p”

Jednak gdy staje się to naprawdę interesujące, jeśli ustawisz ostatni indeks większy niż łańcuch na placu zabaw, pokaże wszystkie łańcuchy zdefiniowane po str: o

Range () wydaje się być funkcją ogólną, więc musi znać typ, z którym ma do czynienia.

Musisz również podać rzeczywisty ciąg, który jest zainteresowany placami zabaw, ponieważ wydaje się, że utrzymuje wszystkie żądła w sekwencji jeden po drugim z ich zmienną nazwą.

Więc

var str:String = "Hello, playground"

var str2:String = "I'm the next string"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,49))

daje „ello, plac zabaw str Jestem następny ciąg str2 ”

działa, nawet jeśli str2 jest zdefiniowane za pomocą let

:)

Peter Roberts
źródło
Zgadzam się, że jest pełny w porównaniu do Celu-C, jednak wszystko mieści się w jednej linii, co stanowi bonus.
Peter Roberts
Cóż, nadal brakuje jednej linii, która faktycznie wypisuje podłańcuch, biorąc pod uwagę obliczony zakres. Podoba mi się niektóre inne rozwiązania, które tworzą rozszerzenia klasy String przy użyciu twojej logiki, ale w rozszerzeniu. To jest ostatecznie rozwiązanie, z którym poszedłem.
Roderic Campbell
Naprawiono błąd, w którym możesz wyjść poza koniec łańcucha. Teraz możesz podać tylko wartość do str.endIndex
Peter Roberts
4

Rob Napier udzielił już świetnej odpowiedzi za pomocą indeksu dolnego. Ale czułem jedną wadę, ponieważ nie ma możliwości sprawdzenia warunków poza granicami. Może to powodować awarię. Więc zmodyfikowałem rozszerzenie i oto jest

extension String {
    subscript (r: Range<Int>) -> String? { //Optional String as return value
        get {
            let stringCount = self.characters.count as Int
            //Check for out of boundary condition
            if (stringCount < r.endIndex) || (stringCount < r.startIndex){
                return nil
            }
            let startIndex = self.startIndex.advancedBy(r.startIndex)

            let endIndex = self.startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

Wyjście poniżej

var str2 = "Hello, World"

var str3 = str2[0...5]
//Hello,
var str4 = str2[0..<5]
//Hello
var str5 = str2[0..<15]
//nil

Dlatego sugeruję, aby zawsze sprawdzać, czy jest to dozwolone

if let string = str[0...5]
{
    //Manipulate your string safely
}
iPrabu
źródło
Masz tam błąd - endIndex powinien być startIndex zaawansowany przez sam endIndex, a nie odległość między indeksami.
Aviad Ben Dov,
3

W Swift3

Na przykład: zmienna „Duke James Thomas”, musimy uzyskać „James”.

let name = "Duke James Thomas"
let range: Range<String.Index> = name.range(of:"James")!
let lastrange: Range<String.Index> = img.range(of:"Thomas")!
var middlename = name[range.lowerBound..<lstrange.lowerBound]
print (middlename)
Sandu
źródło
2

Biorąc stronę Roba Napiera, opracowałem te wspólne rozszerzenia ciągów , z których dwa to:

subscript (r: Range<Int>) -> String
{
    get {
        let startIndex = advance(self.startIndex, r.startIndex)
        let endIndex = advance(self.startIndex, r.endIndex - 1)

        return self[Range(start: startIndex, end: endIndex)]
    }
}

func subString(startIndex: Int, length: Int) -> String
{
    var start = advance(self.startIndex, startIndex)
    var end = advance(self.startIndex, startIndex + length)
    return self.substringWithRange(Range<String.Index>(start: start, end: end))
}

Stosowanie:

"Awesome"[3...7] //"some"
"Awesome".subString(3, length: 4) //"some"
Albert Bori
źródło
Możesz użyć Rangezamiast Range<String.Index>dzięki wnioskowania typu.
Dan Rosenstark,
2

Oto jak uzyskać zakres z ciągu:

var str = "Hello, playground"

let startIndex = advance(str.startIndex, 1)
let endIndex = advance(startIndex, 8)
let range = startIndex..<endIndex
let substr = str[range] //"ello, pl"

Kluczową kwestią jest to, że zamiast liczb całkowitych przekazujesz zakres wartości typu String.Index (właśnie to zwraca zaliczka).

Powodem, dla którego jest to konieczne, jest to, że ciągi w Swift nie mają losowego dostępu (z powodu zmiennej długości znaków Unicode). Ty też nie możesz str[1]. String.Index jest zaprojektowany do pracy z ich wewnętrzną strukturą.

Możesz jednak utworzyć rozszerzenie z indeksem dolnym, który zrobi to za Ciebie, więc możesz po prostu przekazać zakres liczb całkowitych (patrz np . Odpowiedź Roba Napiera ).

Ixx
źródło
2

Próbowałem wymyślić coś Pythonic.

Wszystkie indeksy tutaj są świetne, ale czasy, kiedy naprawdę potrzebuję czegoś prostego, zwykle są, gdy chcę liczyć od tyłu, np string.endIndex.advancedBy(-1)

Obsługuje nilwartości, zarówno dla indeksu początkowego, jak i końcowego, gdzie niloznaczałoby indeks o wartości 0 dla początku, string.characters.countdla końca.

extension String {
    var subString: (Int?) -> (Int?) -> String {
        return { (start) in
            { (end) in
                let startIndex = start ?? 0 < 0 ? self.endIndex.advancedBy(start!) : self.startIndex.advancedBy(start ?? 0)
                let endIndex = end ?? self.characters.count < 0 ? self.endIndex.advancedBy(end!) : self.startIndex.advancedBy(end ?? self.characters.count)

                return startIndex > endIndex ? "" : self.substringWithRange(startIndex ..< endIndex)
            }
        }
    }
}

let dog = "Dog‼🐶"
print(dog.subString(nil)(-1)) // Dog!!

EDYTOWAĆ

Bardziej eleganckie rozwiązanie:

public extension String {
    struct Substring {
        var start: Int?
        var string: String

        public subscript(end: Int?) -> String {
            let startIndex = start ?? 0 < 0 ? string.endIndex.advancedBy(start!) : string.startIndex.advancedBy(start ?? 0)
            let endIndex = end ?? string.characters.count < 0 ? string.endIndex.advancedBy(end!) : string.startIndex.advancedBy(end ?? string.characters.count)

            return startIndex > endIndex ? "" : string.substringWithRange(startIndex ..< endIndex)
        }
    }

    public subscript(start: Int?) -> Substring {
        return Substring(start: start, string: self)
    }
}

let dog = "Dog‼🐶"
print(dog[nil][-1]) // Dog!!
funk7
źródło
1

Najpierw utwórz zakres, a następnie podciąg. Możesz użyć fromIndex..<toIndexskładni w następujący sposób:

let range = fullString.startIndex..<fullString.startIndex.advancedBy(15) // 15 first characters of the string
let substring = fullString.substringWithRange(range)
Johannes
źródło
1

oto przykład, aby szybko pobrać tylko identyfikator wideo .ie (6oL687G0Iso) z całego adresu URL

let str = "https://www.youtube.com/watch?v=6oL687G0Iso&list=PLKmzL8Ib1gsT-5LN3V2h2H14wyBZTyvVL&index=2"
var arrSaprate = str.componentsSeparatedByString("v=")
let start = arrSaprate[1]
let rangeOfID = Range(start: start.startIndex,end:start.startIndex.advancedBy(11))
let substring = start[rangeOfID]
print(substring)
Hardik Thakkar
źródło
1
let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)

Pełną próbkę można zobaczyć tutaj

Svitlana
źródło
0

Cóż, miałem ten sam problem i rozwiązałem go za pomocą funkcji „bridgeToObjectiveC ()”:

var helloworld = "Hello World!"
var world = helloworld.bridgeToObjectiveC().substringWithRange(NSMakeRange(6,6))
println("\(world)") // should print World!

Należy zauważyć, że w tym przykładzie substringWithRange w połączeniu z NSMakeRange biorą udział w łańcuchu zaczynając od indeksu 6 (znak „W”) i kończąc na indeksie 6 + 6 pozycji do przodu (znak „!”)

Twoje zdrowie.

sittisal
źródło
Nienawidzę tego, ale wydaje mi się, że to jedyna rzecz, która działa dla mnie w wersji Beta 4.
Roderic Campbell
0

Możesz użyć dowolnej metody podciągów w rozszerzeniu Swift String, które napisałem https://bit.ly/JString .

var string = "hello"
var sub = string.substringFrom(3) // or string[3...5]
println(sub)// "lo"
William Falcon
źródło
0

Jeśli masz NSRange, pomost do NSStringdziała płynnie. Na przykład robiłem trochę pracy UITextFieldDelegatei szybko chciałem obliczyć nową wartość ciągu, gdy zapytano, czy powinna zastąpić zakres.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
    println("Got new string: ", newString)
}
joslinm
źródło
0

Proste rozszerzenie dla String:

extension String {

    func substringToIndex(index: Int) -> String {
        return self[startIndex...startIndex.advancedBy(min(index, characters.count - 1))]
    }
}
Bartłomiej Semańczyk
źródło
0

Jeśli nie zależy Ci na wydajności ... jest to prawdopodobnie najbardziej zwięzłe rozwiązanie w Swift 4

extension String {
    subscript(range: CountableClosedRange<Int>) -> String {
        return enumerated().filter{$0.offset >= range.first! && $0.offset < range.last!}
            .reduce(""){$0 + String($1.element)}
    }
}

Umożliwia zrobienie czegoś takiego:

let myStr = "abcd"
myStr[0..<2] // produces "ab"
Jiachen Ren
źródło