Do czego potrzebujesz tej gamy? Używa szybkich łańcuchów Range<String.Index>, ale czasami jest to konieczne do pracy z NSStringi NSRange, więc przydałoby się trochę więcej kontekstu. - Ale spójrz na stackoverflow.com/questions/24092884/… .
Martin R
Odpowiedzi:
258
Zaktualizowano dla Swift 4
Swift zakresy są bardziej złożone niż NSRangei nie były łatwiejsze w Swift 3. Jeśli chcesz spróbować zrozumieć przyczyny tej złożoności, przeczytaj to i to . Pokażę ci tylko, jak je tworzyć i kiedy możesz ich używać.
Zamknięte zakresy: a...b
Ten operator zakresu tworzy zakres Swift, który zawiera zarówno element, jak ai element b, nawet jeśli bjest to maksymalna możliwa wartość dla typu (np Int.max.). Istnieją dwa różne typy zamkniętych zakresów: ClosedRangei CountableClosedRange.
1. ClosedRange
Elementy wszystkich zakresów w Swift są porównywalne (tj. Są zgodne z protokołem Comparable). To umożliwia dostęp do elementów w zakresie z kolekcji. Oto przykład:
let myRange:ClosedRange=1...3let myArray =["a","b","c","d","e"]
myArray[myRange]//["b","c","d"]
Jednak a ClosedRangenie jest policzalne (tj. Nie jest zgodne z protokołem Sequence). Oznacza to, że nie możesz iterować po elementach za pomocą forpętli. Do tego potrzebujesz CountableClosedRange.
2. CountableClosedRange
Jest to podobne do poprzedniego, z wyjątkiem tego, że zakres można również iterować.
let myRange:CountableClosedRange=1...3let myArray =["a","b","c","d","e"]
myArray[myRange]// ["b", "c", "d"]
for index in myRange {
print(myArray[index])}
Zakresy półotwarte: a..<b
Ten operator zakresu obejmuje element, aale nie zawiera elementu b. Podobnie jak powyżej, istnieją dwa różne typy półotwartych zakresów: Rangei CountableRange.
1. Range
Podobnie jak w przypadku ClosedRange, możesz uzyskać dostęp do elementów kolekcji za pomocą pliku Range. Przykład:
let myRange:Range=1..<3let myArray =["a","b","c","d","e"]
myArray[myRange]//["b","c"]
Ponownie jednak nie możesz iterować po pliku Range ponieważ jest on tylko porównywalny, a nie dający się usunąć.
2. CountableRange
A CountableRangepozwala na iterację.
let myRange:CountableRange=1..<3let myArray =["a","b","c","d","e"]
myArray[myRange]// ["b", "c"]
for index in myRange {
print(myArray[index])}
NSRange
Możesz (musisz) nadal używać NSRangew Swift ( na przykład podczas tworzenia przypisanych ciągów ), więc warto wiedzieć, jak je utworzyć.
let myNSRange =NSRange(location:3, length:2)
Zauważ, że jest to lokalizacja i długość , a nie indeks początkowy i końcowy. Poniższy przykład ma podobne znaczenie do serii Swift 3..<5. Jednak ze względu na różne typy nie można ich stosować zamiennie.
Zakresy z ciągami
Operatory ...i ..<zakresów to skrótowy sposób tworzenia zakresów. Na przykład:
let myRange =1..<3
Długa droga do stworzenia tego samego zakresu byłaby
let myRange =CountableRange<Int>(uncheckedBounds:(lower:1, upper:3))//1..<3
Widać, że typ indeksu to Int. To jednak nie działa String, ponieważ ciągi składają się ze znaków i nie wszystkie znaki są tego samego rozmiaru. (Przeczytaj to aby uzyskać więcej informacji). Na przykład emoji, takie jak 😀, zajmuje więcej miejsca niż litera „b”.
Problem z NSRange
Wypróbować NSRangeoraz NSStringz emotikonami, a zobaczysz co mam na myśli. Bół głowy.
let myNSRange =NSRange(location:1, length:3)let myNSString:NSString="abcde"
myNSString.substring(with: myNSRange)// "bcd"
let myNSString2:NSString="a😀cde"
myNSString2.substring(with: myNSRange)//"😀c"Whereis the "d"!?
Buźka zajmuje do przechowywania dwie jednostki kodu UTF-16, więc daje nieoczekiwany rezultat nieuwzględniania litery „d”.
Szybkie rozwiązanie
Z tego powodu, ze Swift Strings użyć Range<String.Index>, nie Range<Int>. Indeks ciągu jest obliczany na podstawie określonego ciągu, dzięki czemu wie, czy są jakieś emoji lub rozszerzone klastry grafemów.
W Swift 4 rzeczy zostały nieco uproszczone. Zawsze, gdy można wywnioskować punkt początkowy lub końcowy zakresu, możesz go pominąć.
Int
Do iteracji po kolekcjach można używać jednostronnych zakresów liczb całkowitych. Oto kilka przykładów z dokumentacji .
// iterate from index 2 to the end of the array
for name in names[2...]{
print(name)}// iterate from the beginning of the array to index 2
for name in names[...2]{
print(name)}// iterate from the beginning of the array up to but not including index 2
for name in names[..<2]{
print(name)}// the range from negative infinity to 5. You can't iterate forward
// over this because the starting point in unknown.
let range =...5
range.contains(7)// false
range.contains(4)// true
range.contains(-1)// true
// You can iterate over this but it will be an infinate loop
// so you have to break out at some point.
let range =5...
Strunowy
Działa to również z zakresami typu String. Jeśli tworzysz zakres z str.startIndexlub str.endIndexna jednym końcu, możesz to zostawić. Kompilator wywnioskuje to.
Dany
var str ="Hello, playground"let index = str.index(str.startIndex, offsetBy:5)let myRange =..<index //Hello
Możesz przejść z indeksu do str.endIndex przy użyciu ...
var str ="Hello, playground"let index = str.index(str.endIndex, offsetBy:-10)let myRange = index...// playground
Mój komentarz dotyczy podrozdziału „Problem z NSRange”. NSStringwewnętrznie przechowuje swoje znaki w kodowaniu UTF-16. Pełny skalar Unicode jest 21-bitowy. Znak uśmiechniętej twarzy ( U+1F600) nie może być przechowywany w pojedynczej 16-bitowej jednostce kodu, więc jest rozłożony na 2. NSRangeliczby oparte na 16-bitowych jednostkach kodu. W tym przykładzie, 3 jednostki kodu reprezentują tylko 2 znaki
var start = str.startIndex // Start at the string's start index
var end = advance(str.startIndex,5)// Take start index and advance 5 characters forward
var range:Range<String.Index>=Range<String.Index>(start: start,end: end)let firstFiveDigit = str.substringWithRange(range)
print(firstFiveDigit)
Widzę, że mają typ danych „Zakres”. Więc jak mogę tego używać?
vichhai
@vichhai Czy możesz podać więcej informacji, które chcesz wykorzystać?
Dharmbir Singh
Jak w celu c: zakres NSRange;
vichhai
1
Zaskakujące jest dla mnie to, że nawet w języku Swift 4 nadal nie ma prostego natywnego sposobu wyrażenia zakresu String za pomocą Int. Jedynymi metodami typu String, które pozwalają podać Int jako sposób uzyskania podciągu według zakresu, są prefixi suffix.
Przydatne jest mieć pod ręką kilka narzędzi do konwersji, abyśmy mogli mówić jak NSRange podczas mówienia do String. Oto narzędzie, które przyjmuje lokalizację i długość, podobnie jak NSRange, i zwraca Range<String.Index>:
Na przykład, "hello".range(0,1)"to Range<String.Index>objęcie pierwszego znaku "hello". Jako bonus, zezwoliłem na negatywne lokalizacje: "hello".range(-1,1)"jest Range<String.Index>to ostatnia postać "hello".
Przydatne jest również przekonwertowanie a Range<String.Index>na NSRange, w tych momentach, kiedy musisz rozmawiać z Cocoa (na przykład w przypadku radzenia sobie z zakresami atrybutów NSAttributedString). Swift 4 zapewnia natywny sposób na zrobienie tego:
let nsrange =NSRange(range,in:s)//where s is the string
Możemy zatem napisać inne narzędzie, w którym przechodzimy bezpośrednio z lokalizacji String i długości do NSRange:
let nsRange =NSRange(location: someInt, length: someInt)
jak w
let myNSString = bigTOTPCode asNSString//12345678
let firstDigit = myNSString.substringWithRange(NSRange(location:0, length:1))//1
let secondDigit = myNSString.substringWithRange(NSRange(location:1, length:1))//2
let thirdDigit = myNSString.substringWithRange(NSRange(location:2, length:4))//3456
Range<String.Index>
, ale czasami jest to konieczne do pracy zNSString
iNSRange
, więc przydałoby się trochę więcej kontekstu. - Ale spójrz na stackoverflow.com/questions/24092884/… .Odpowiedzi:
Zaktualizowano dla Swift 4
Swift zakresy są bardziej złożone niż
NSRange
i nie były łatwiejsze w Swift 3. Jeśli chcesz spróbować zrozumieć przyczyny tej złożoności, przeczytaj to i to . Pokażę ci tylko, jak je tworzyć i kiedy możesz ich używać.Zamknięte zakresy:
a...b
Ten operator zakresu tworzy zakres Swift, który zawiera zarówno element, jak
a
i elementb
, nawet jeślib
jest to maksymalna możliwa wartość dla typu (npInt.max
.). Istnieją dwa różne typy zamkniętych zakresów:ClosedRange
iCountableClosedRange
.1.
ClosedRange
Elementy wszystkich zakresów w Swift są porównywalne (tj. Są zgodne z protokołem Comparable). To umożliwia dostęp do elementów w zakresie z kolekcji. Oto przykład:
Jednak a
ClosedRange
nie jest policzalne (tj. Nie jest zgodne z protokołem Sequence). Oznacza to, że nie możesz iterować po elementach za pomocąfor
pętli. Do tego potrzebujeszCountableClosedRange
.2.
CountableClosedRange
Jest to podobne do poprzedniego, z wyjątkiem tego, że zakres można również iterować.
Zakresy półotwarte:
a..<b
Ten operator zakresu obejmuje element,
a
ale nie zawiera elementub
. Podobnie jak powyżej, istnieją dwa różne typy półotwartych zakresów:Range
iCountableRange
.1.
Range
Podobnie jak w przypadku
ClosedRange
, możesz uzyskać dostęp do elementów kolekcji za pomocą plikuRange
. Przykład:Ponownie jednak nie możesz iterować po pliku
Range
ponieważ jest on tylko porównywalny, a nie dający się usunąć.2.
CountableRange
A
CountableRange
pozwala na iterację.NSRange
Możesz (musisz) nadal używać
NSRange
w Swift ( na przykład podczas tworzenia przypisanych ciągów ), więc warto wiedzieć, jak je utworzyć.Zauważ, że jest to lokalizacja i długość , a nie indeks początkowy i końcowy. Poniższy przykład ma podobne znaczenie do serii Swift
3..<5
. Jednak ze względu na różne typy nie można ich stosować zamiennie.Zakresy z ciągami
Operatory
...
i..<
zakresów to skrótowy sposób tworzenia zakresów. Na przykład:Długa droga do stworzenia tego samego zakresu byłaby
Widać, że typ indeksu to
Int
. To jednak nie działaString
, ponieważ ciągi składają się ze znaków i nie wszystkie znaki są tego samego rozmiaru. (Przeczytaj to aby uzyskać więcej informacji). Na przykład emoji, takie jak 😀, zajmuje więcej miejsca niż litera „b”.Problem z NSRange
Wypróbować
NSRange
orazNSString
z emotikonami, a zobaczysz co mam na myśli. Bół głowy.Buźka zajmuje do przechowywania dwie jednostki kodu UTF-16, więc daje nieoczekiwany rezultat nieuwzględniania litery „d”.
Szybkie rozwiązanie
Z tego powodu, ze Swift Strings użyć
Range<String.Index>
, nieRange<Int>
. Indeks ciągu jest obliczany na podstawie określonego ciągu, dzięki czemu wie, czy są jakieś emoji lub rozszerzone klastry grafemów.Przykład
Zakresy jednostronne:
a...
i...b
i..<b
W Swift 4 rzeczy zostały nieco uproszczone. Zawsze, gdy można wywnioskować punkt początkowy lub końcowy zakresu, możesz go pominąć.
Int
Do iteracji po kolekcjach można używać jednostronnych zakresów liczb całkowitych. Oto kilka przykładów z dokumentacji .
Strunowy
Działa to również z zakresami typu String. Jeśli tworzysz zakres z
str.startIndex
lubstr.endIndex
na jednym końcu, możesz to zostawić. Kompilator wywnioskuje to.Dany
Możesz przejść z indeksu do str.endIndex przy użyciu
...
Zobacz też:
Uwagi
Dalsze badanie
źródło
NSString
wewnętrznie przechowuje swoje znaki w kodowaniu UTF-16. Pełny skalar Unicode jest 21-bitowy. Znak uśmiechniętej twarzy (U+1F600
) nie może być przechowywany w pojedynczej 16-bitowej jednostce kodu, więc jest rozłożony na 2.NSRange
liczby oparte na 16-bitowych jednostkach kodu. W tym przykładzie, 3 jednostki kodu reprezentują tylko 2 znakiXcode 8 beta 2 • Swift 3
Xcode 7 • Swift 2.0
lub po prostu
źródło
zwroty...
źródło
Użyj w ten sposób
Wyjście: Hello
źródło
Zaskakujące jest dla mnie to, że nawet w języku Swift 4 nadal nie ma prostego natywnego sposobu wyrażenia zakresu String za pomocą Int. Jedynymi metodami typu String, które pozwalają podać Int jako sposób uzyskania podciągu według zakresu, są
prefix
isuffix
.Przydatne jest mieć pod ręką kilka narzędzi do konwersji, abyśmy mogli mówić jak NSRange podczas mówienia do String. Oto narzędzie, które przyjmuje lokalizację i długość, podobnie jak NSRange, i zwraca
Range<String.Index>
:Na przykład,
"hello".range(0,1)"
toRange<String.Index>
objęcie pierwszego znaku"hello"
. Jako bonus, zezwoliłem na negatywne lokalizacje:"hello".range(-1,1)"
jestRange<String.Index>
to ostatnia postać"hello"
.Przydatne jest również przekonwertowanie a
Range<String.Index>
na NSRange, w tych momentach, kiedy musisz rozmawiać z Cocoa (na przykład w przypadku radzenia sobie z zakresami atrybutów NSAttributedString). Swift 4 zapewnia natywny sposób na zrobienie tego:Możemy zatem napisać inne narzędzie, w którym przechodzimy bezpośrednio z lokalizacji String i długości do NSRange:
źródło
Jeśli ktoś chce stworzyć obiekt NSRange może stworzyć jako:
stworzy to zakres z pozycją 0 i długością 5
źródło
źródło
Stworzyłem następujące rozszerzenie:
przykład użycia:
źródło
Możesz używać w ten sposób
jak w
źródło
Chce to zrobić:
Ale niestety nie mogę napisać własnego indeksu, ponieważ ten nienawidzony zajmuje miejsce na nazwisko.
Możemy to jednak zrobić:
Po prostu dodaj to do swojego projektu:
źródło