Зачем вам диапазон? Строки Swift используются Range<String.Index>, но иногда необходимо работать с NSStringи NSRange, поэтому было бы полезно добавить дополнительный контекст. - Но посмотрите stackoverflow.com/questions/24092884/… .
Martin R
Ответы:
258
Обновлено для Swift 4
Диапазоны Swift сложнее NSRange, и в Swift 3 они не стали проще. Если вы хотите попытаться понять причины, лежащие в основе этой сложности, прочтите это и это . Я просто покажу вам, как их создавать и когда вы можете их использовать.
Закрытые диапазоны: a...b
Этот оператор диапазона создает диапазон Swift, который включает как элемент, так aи элемент b, даже если bэто максимально возможное значение для типа (например, Int.max). Есть два разных типа закрытых диапазонов: ClosedRangeи CountableClosedRange.
1. ClosedRange
Элементы всех диапазонов в Swift сопоставимы (т. Е. Соответствуют протоколу Comparable). Это позволяет вам получить доступ к элементам в диапазоне из коллекции. Вот пример:
let myRange:ClosedRange=1...3let myArray =["a","b","c","d","e"]
myArray[myRange]//["b","c","d"]
Однако a ClosedRangeне является счетным (т. Е. Не соответствует протоколу Sequence). Это означает, что вы не можете перебирать элементы с помощью forцикла. Для этого вам понадобится CountableClosedRange.
2. CountableClosedRange
Это похоже на предыдущее, за исключением того, что теперь диапазон также можно перебирать.
let myRange:CountableClosedRange=1...3let myArray =["a","b","c","d","e"]
myArray[myRange]// ["b", "c", "d"]
for index in myRange {
print(myArray[index])}
Полуоткрытые диапазоны: a..<b
Этот оператор диапазона включает элемент, aно не элемент b. Как и выше, есть два разных типа полуоткрытых диапазонов: Rangeи CountableRange.
1. Range
Как и в случае с ClosedRange, вы можете получить доступ к элементам коллекции с помощью Range. Пример:
let myRange:Range=1..<3let myArray =["a","b","c","d","e"]
myArray[myRange]//["b","c"]
Опять же, вы не можете перебирать a, Rangeпотому что он только сопоставим, а не стримируем.
2. CountableRange
A CountableRangeразрешает итерацию.
let myRange:CountableRange=1..<3let myArray =["a","b","c","d","e"]
myArray[myRange]// ["b", "c"]
for index in myRange {
print(myArray[index])}
NSRange
Вы можете (должны) время от времени использовать NSRangeSwift (например, при создании строк с атрибутами ), поэтому полезно знать, как их создать.
let myNSRange =NSRange(location:3, length:2)
Обратите внимание, что это местоположение и длина , а не начальный и конечный индексы. Пример здесь аналогичен по значению диапазону Swift 3..<5. Однако, поскольку типы разные, они не взаимозаменяемы.
Диапазоны со струнами
Операторы ...и ..<диапазоны - это сокращенный способ создания диапазонов. Например:
let myRange =1..<3
Длинный способ создать такой же диапазон был бы
let myRange =CountableRange<Int>(uncheckedBounds:(lower:1, upper:3))//1..<3
Вы можете видеть, что тип индекса здесь Int. Однако это не работает String, потому что строки состоят из символов, а не все символы одинакового размера. (Прочтите это, чтобы узнать больше.) Смайлик, например 😀, занимает больше места, чем буква «b».
Проблема с NSRange
Попробуйте поэкспериментировать с NSRangeи NSStringс смайликами , и вы увидите , что я имею в виду. Головная боль.
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"!?
Смайлик требует для хранения двух кодовых единиц UTF-16, поэтому он дает неожиданный результат без включения буквы «d».
Быстрое решение
Из - за этого, с Swift Струны вы используете Range<String.Index>, не Range<Int>. String Index рассчитывается на основе конкретной строки, поэтому он знает, есть ли какие-либо смайлики или расширенные кластеры графем.
В Swift 4 все было немного упрощено. Если можно определить начальную или конечную точку диапазона, вы можете не указывать ее.
Int
Вы можете использовать односторонние целочисленные диапазоны для перебора коллекций. Вот несколько примеров из документации .
// 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...
Строка
Это также работает с диапазонами String. Если вы составляете диапазон с одним концом str.startIndexили str.endIndexна одном конце, вы можете не использовать его. Компилятор это сделает.
Дано
var str ="Hello, playground"let index = str.index(str.startIndex, offsetBy:5)let myRange =..<index //Hello
Вы можете перейти от индекса к str.endIndex, используя ...
var str ="Hello, playground"let index = str.index(str.endIndex, offsetBy:-10)let myRange = index...// playground
Мой комментарий относится к подразделу «Проблема с NSRange». NSStringвнутри хранит свои символы в кодировке UTF-16. Полный скаляр Юникода составляет 21 бит. Символ ухмыляющегося лица ( U+1F600) не может быть сохранен в единственной 16-битной кодовой единице, поэтому он разбивается на 2. NSRangeсчетчики на основе 16-битных кодовых единиц. В этом примере 3 кодовых единицы представляют только 2 символа
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)
Я вижу, что у них есть тип данных "Диапазон". Так как я могу его использовать?
vichhai
@vichhai Не могли бы вы рассказать подробнее, где вы хотите использовать?
Дхармбир Сингх
Как в объекте c: диапазон NSRange;
vichhai
1
Я нахожу удивительным, что даже в Swift 4 до сих пор нет простого собственного способа выразить диапазон String с помощью Int. Единственные методы String, которые позволяют предоставить Int как способ получения подстроки по диапазону, - это prefixи suffix.
Полезно иметь под рукой некоторые утилиты преобразования, чтобы мы могли говорить как NSRange при обращении к String. Вот утилита, которая, как и NSRange, принимает местоположение и длину и возвращает Range<String.Index>:
Например, "hello".range(0,1)"это Range<String.Index>охватывает первый символ "hello". В качестве бонуса я разрешил отрицательные местоположения: "hello".range(-1,1)"это Range<String.Index>обнимающий последний символ "hello".
Полезно также преобразовать a Range<String.Index>в NSRange в те моменты, когда вам нужно поговорить с Какао (например, при работе с диапазонами атрибутов NSAttributedString). Swift 4 предоставляет собственный способ сделать это:
let nsrange =NSRange(range,in:s)//where s is the string
Таким образом, мы можем написать другую утилиту, в которой мы перейдем непосредственно от местоположения и длины String к NSRange:
let nsRange =NSRange(location: someInt, length: someInt)
как в
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>
, но иногда необходимо работать сNSString
иNSRange
, поэтому было бы полезно добавить дополнительный контекст. - Но посмотрите stackoverflow.com/questions/24092884/… .Ответы:
Обновлено для Swift 4
Диапазоны Swift сложнее
NSRange
, и в Swift 3 они не стали проще. Если вы хотите попытаться понять причины, лежащие в основе этой сложности, прочтите это и это . Я просто покажу вам, как их создавать и когда вы можете их использовать.Закрытые диапазоны:
a...b
Этот оператор диапазона создает диапазон Swift, который включает как элемент, так
a
и элементb
, даже еслиb
это максимально возможное значение для типа (например,Int.max
). Есть два разных типа закрытых диапазонов:ClosedRange
иCountableClosedRange
.1.
ClosedRange
Элементы всех диапазонов в Swift сопоставимы (т. Е. Соответствуют протоколу Comparable). Это позволяет вам получить доступ к элементам в диапазоне из коллекции. Вот пример:
Однако a
ClosedRange
не является счетным (т. Е. Не соответствует протоколу Sequence). Это означает, что вы не можете перебирать элементы с помощьюfor
цикла. Для этого вам понадобитсяCountableClosedRange
.2.
CountableClosedRange
Это похоже на предыдущее, за исключением того, что теперь диапазон также можно перебирать.
Полуоткрытые диапазоны:
a..<b
Этот оператор диапазона включает элемент,
a
но не элементb
. Как и выше, есть два разных типа полуоткрытых диапазонов:Range
иCountableRange
.1.
Range
Как и в случае с
ClosedRange
, вы можете получить доступ к элементам коллекции с помощьюRange
. Пример:Опять же, вы не можете перебирать a,
Range
потому что он только сопоставим, а не стримируем.2.
CountableRange
A
CountableRange
разрешает итерацию.NSRange
Вы можете (должны) время от времени использовать
NSRange
Swift (например, при создании строк с атрибутами ), поэтому полезно знать, как их создать.Обратите внимание, что это местоположение и длина , а не начальный и конечный индексы. Пример здесь аналогичен по значению диапазону Swift
3..<5
. Однако, поскольку типы разные, они не взаимозаменяемы.Диапазоны со струнами
Операторы
...
и..<
диапазоны - это сокращенный способ создания диапазонов. Например:Длинный способ создать такой же диапазон был бы
Вы можете видеть, что тип индекса здесь
Int
. Однако это не работаетString
, потому что строки состоят из символов, а не все символы одинакового размера. (Прочтите это, чтобы узнать больше.) Смайлик, например 😀, занимает больше места, чем буква «b».Проблема с NSRange
Попробуйте поэкспериментировать с
NSRange
иNSString
с смайликами , и вы увидите , что я имею в виду. Головная боль.Смайлик требует для хранения двух кодовых единиц UTF-16, поэтому он дает неожиданный результат без включения буквы «d».
Быстрое решение
Из - за этого, с Swift Струны вы используете
Range<String.Index>
, неRange<Int>
. String Index рассчитывается на основе конкретной строки, поэтому он знает, есть ли какие-либо смайлики или расширенные кластеры графем.пример
Односторонние диапазоны:
a...
и...b
и..<b
В Swift 4 все было немного упрощено. Если можно определить начальную или конечную точку диапазона, вы можете не указывать ее.
Int
Вы можете использовать односторонние целочисленные диапазоны для перебора коллекций. Вот несколько примеров из документации .
Строка
Это также работает с диапазонами String. Если вы составляете диапазон с одним концом
str.startIndex
илиstr.endIndex
на одном конце, вы можете не использовать его. Компилятор это сделает.Дано
Вы можете перейти от индекса к str.endIndex, используя
...
Смотрите также:
Ноты
Дальнейшее обучение
источник
NSString
внутри хранит свои символы в кодировке UTF-16. Полный скаляр Юникода составляет 21 бит. Символ ухмыляющегося лица (U+1F600
) не может быть сохранен в единственной 16-битной кодовой единице, поэтому он разбивается на 2.NSRange
счетчики на основе 16-битных кодовых единиц. В этом примере 3 кодовых единицы представляют только 2 символаXcode 8 beta 2 • Swift 3
Xcode 7 • Swift 2.0
или просто
источник
возвращается ...
источник
Используйте как это
Выход: Здравствуйте
источник
Я нахожу удивительным, что даже в Swift 4 до сих пор нет простого собственного способа выразить диапазон String с помощью Int. Единственные методы String, которые позволяют предоставить Int как способ получения подстроки по диапазону, - это
prefix
иsuffix
.Полезно иметь под рукой некоторые утилиты преобразования, чтобы мы могли говорить как NSRange при обращении к String. Вот утилита, которая, как и NSRange, принимает местоположение и длину и возвращает
Range<String.Index>
:Например,
"hello".range(0,1)"
этоRange<String.Index>
охватывает первый символ"hello"
. В качестве бонуса я разрешил отрицательные местоположения:"hello".range(-1,1)"
этоRange<String.Index>
обнимающий последний символ"hello"
.Полезно также преобразовать a
Range<String.Index>
в NSRange в те моменты, когда вам нужно поговорить с Какао (например, при работе с диапазонами атрибутов NSAttributedString). Swift 4 предоставляет собственный способ сделать это:Таким образом, мы можем написать другую утилиту, в которой мы перейдем непосредственно от местоположения и длины String к NSRange:
источник
Если кто-то хочет создать объект NSRange, можно создать как:
это создаст диапазон с позицией 0 и длиной 5
источник
источник
Я создал следующее расширение:
пример использования:
источник
Вы можете использовать это так
как в
источник
Я хочу сделать это:
Но, к сожалению, я не могу написать собственный подстрочный индекс, потому что ненавистный занимает пространство имен.
Однако мы можем сделать это:
Просто добавьте это в свой проект:
источник