Как работает строковая подстрока в Swift
Я обновлял часть своего старого кода и отвечал на Swift 3, но когда я добрался до Swift Strings и Indexing с подстроками, все стало запутанным.
В частности, я пытался сделать следующее:
let str = "Hello, playground" let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) let prefix = str.substringWithRange(prefixRange)
где вторая строка давала мне следующую ошибку
- Bash: манипуляция со строками (знак процента)
- Равная высота элементов внутри элемента сетки с раскладкой сетки CSS
- Bash не анализирует кавычки при преобразовании строки в аргументы
- Скрыть ввод в командной строке
- Поиск строки Java, игнорирующий акценты
Значение типа ‘String’ не имеет подстроки‘WithRange ‘
Я вижу, что у String
теперь есть следующие методы:
str.substring(to: String.Index) str.substring(from: String.Index) str.substring(with: Range)
Сначала это меня сбивало с толку, поэтому я начал играть по индексу и диапазону . Это вопрос и ответ для подстроки. Я добавляю ответ ниже, чтобы показать, как они используются.
Все приведенные ниже примеры
var str = "Hello, playground"
Swift 4
Строки получили довольно большой капитальный ремонт в Swift 4. Когда вы получаете некоторую подстроку из строки, теперь вы получаете тип Substring
а не String
. Почему это? Строки – это типы значений в Swift. Это означает, что если вы используете одну String для создания новой, ее нужно скопировать. Это хорошо для стабильности (никто другой не собирается менять его без вашего ведома), но плохо для эффективности.
Подстрока, с другой стороны, является ссылкой на исходную строку, из которой она появилась. Вот изображение из документации, иллюстрирующей это.
Никакое копирование не требуется, поэтому его гораздо эффективнее использовать. Однако представьте, что у вас есть десятизначная подстрока из миллиона символов String. Поскольку подстрока ссылается на String, система должна была бы удерживаться на всей строке до тех пор, пока подстрока находится вокруг. Таким образом, всякий раз, когда вы закончите манипулировать своей Подстрокой, преобразуйте ее в String.
let myString = String(mySubstring)
Это скопирует только подстроку, и старая строка может быть собрана мусором. Подстроки (как тип) должны быть короткими.
Еще одним большим улучшением в Swift 4 является то, что Strings являются коллекциями (снова). Это означает, что независимо от того, что вы можете сделать в коллекции, вы можете сделать это с помощью String (использовать индексы, перебирать символы, фильтровать и т. Д.).
В следующих примерах показано, как получить подстроку в Swift.
Получение подстрок
Вы можете получить подстроку из строки, используя индексы или ряд других методов (например, prefix
, suffix
, split
). Однако вам все равно нужно использовать String.Index
а не индекс Int
для диапазона. (См. Мой другой ответ, если вам нужна помощь.)
Начало строки
Вы можете использовать индекс (обратите внимание на односторонний диапазон Swift 4):
let index = str.index(str.startIndex, offsetBy: 5) let mySubstring = str[..
или prefix
:
let index = str.index(str.startIndex, offsetBy: 5) let mySubstring = str.prefix(upTo: index) // Hello
или даже проще:
let mySubstring = str.prefix(5) // Hello
Конец строки
Использование индексов:
let index = str.index(str.endIndex, offsetBy: -10) let mySubstring = str[index...] // playground
или suffix
:
let index = str.index(str.endIndex, offsetBy: -10) let mySubstring = str.suffix(from: index) // playground
или даже проще:
let mySubstring = str.suffix(10) // playground
Обратите внимание, что при использовании suffix(from: index)
мне приходилось возвращаться с конца с помощью -10
. Это не обязательно при использовании suffix(x)
, который просто принимает последние x
символов строки.
Диапазон в строке
Снова мы просто используем индексы здесь.
let start = str.index(str.startIndex, offsetBy: 7) let end = str.index(str.endIndex, offsetBy: -6) let range = start..
Преобразование Substring
в String
Не забывайте, что когда вы готовы сохранить свою подстроку, вы должны преобразовать ее в String
чтобы можно было очистить память старой строки.
let myString = String(mySubstring)
Использование расширения индекса Int
?
Я не решаюсь использовать расширение индекса на основе Int
после прочтения статьи Strings in Swift 3 от Airspeed Velocity и Ole Begemann. Хотя в Swift 4 Strings являются коллекциями, команда Swift специально не использовала индексы Int
. Это все еще String.Index
. Это связано с тем, что Swift Characters состоит из разных номеров кодов Unicode. Фактический индекс должен быть однозначно рассчитан для каждой строки.
Должен сказать, я надеюсь, что команда Swift найдет способ абстрагироваться от String.Index
в будущем. Но до тех пор я решил использовать их API. Это помогает мне помнить, что манипуляции с строками - это не просто простые поисковые запросы Int
.
Я очень разочарован в модели доступа String от Swift: все должно быть Index
. Все, что я хочу, – это доступ к i-му символу строки с использованием Int
, а не неуклюжий индекс и продвижение (что происходит с каждым основным выпуском). Поэтому я сделал расширение для 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 substring(from: fromIndex) } func substring(to: Int) -> String { let toIndex = index(from: to) return substring(to: toIndex) } func substring(with r: Range) -> String { let startIndex = index(from: r.lowerBound) let endIndex = index(from: r.upperBound) return substring(with: startIndex..
Swift 4 Расширение:
extension String { subscript(_ range: CountableRange) -> String { let idx1 = index(startIndex, offsetBy: max(0, range.lowerBound)) let idx2 = index(startIndex, offsetBy: min(self.count, range.upperBound)) return String(self[idx1..
Применение:
let s = "hello" s[0..<3] // "hel" s[3..
Или unicode:
let s = "😎🤣😋" s[0..<1] // "😎"
Swift 4
В быстрой строке 4 соответствует Collection
. Вместо substring
мы должны теперь использовать subscript.
Поэтому, если вы хотите вырезать только слово "play"
из "Hello, playground"
, вы можете сделать это следующим образом:
var str = "Hello, playground" let start = str.index(str.startIndex, offsetBy: 7) let end = str.index(str.endIndex, offsetBy: -6) let result = str[start..
Интересно знать, что это даст вам Substring
вместо String
. Это быстро и эффективно, поскольку Substring
разделяет его хранилище с исходной строкой. Однако обмен памяти таким образом также может легко привести к утечкам памяти.
Вот почему вы должны скопировать результат в новую строку, как только вы захотите очистить исходную строку. Вы можете сделать это, используя обычный конструктор:
let newString = String(result)
Дополнительную информацию о новом classе Substring
можно найти в [Документация Apple]. 1
Итак, если вы, например, получаете Range
в результате NSRegularExpression
, вы можете использовать следующее расширение:
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..
У меня была такая же начальная реакция. Я тоже был расстроен тем, как синтаксис и объекты так резко изменяются во всех основных выпусках.
Тем не менее, я понял из опыта, что я всегда в конечном итоге страдает от последствий попыток борьбы с «изменением», как с многобайтовыми символами, что неизбежно, если вы смотрите на глобальную аудиторию.
Поэтому я решил признать и уважать усилия, прилагаемые инженерами Apple, и внести свою часть, понимая их мышление, когда они придумали этот «ужасный» подход.
Вместо того, чтобы создавать расширения, которые являются всего лишь обходным путем, чтобы облегчить вашу жизнь (я не говорю, что они ошибаются или дорогие), почему бы не понять, как Strings теперь предназначены для работы.
Например, у меня был этот код, который работал над 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)
и, отказавшись от попытки использовать тот же подход, например, используя подстроки, я наконец понял концепцию обработки строк как двунаправленную коллекцию, для которой я закончил эту версию того же кода:
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))
Надеюсь, это способствует …
Я новичок в Swift 3, но смотрю синтаксис String
(index) для аналогии. Я считаю, что индекс похож на «указатель», ограниченный строкой, и Int может помочь в качестве независимого объекта. Используя синтаксис base + offset, мы можем получить i-й символ из строки с кодом ниже:
let s = "abcdefghi" let i = 2 print (s[s.index(s.startIndex, offsetBy:i)]) // print c
Для ряда символов (индексов) из строки с использованием синтаксиса String (range) мы можем получить от i-го до f-го символа с помощью следующего кода:
let f = 6 print (s[s.index(s.startIndex, offsetBy:i )..
Для подстроки (диапазона) из строки с использованием String.substring (range) мы можем получить подстроку, используя следующий код:
print (s.substring (with:s.index(s.startIndex, offsetBy:i )..
Заметки:
-
I-й и f-й начинаются с 0.
-
Для f-го я использую offsetBY: f + 1, потому что диапазон использования подписки ... <(полуоткрытый оператор), не включает f-ю позицию.
-
Конечно, должны включать проверки ошибок, таких как неверный индекс.
Вот функция, которая возвращает подстроку заданной подстроки, когда указаны начальные и конечные индексы. Для полной справки вы можете ознакомиться с приведенными ниже ссылками.
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..
Вот ссылка на сообщение в блоге, которое я создал, чтобы справиться с строковыми манипуляциями в swift. Строковые манипуляции в быстрых (Covers swift 4)
Или вы можете увидеть этот смысл на github
Такое же разочарование, это не должно быть так сложно …
Я собрал этот пример получения позиций для подстроки (-ов) из более крупного текста:
// // 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? = ["Why", "substrings", "Swift3"] FindSubString(inputStr: Bigstring, subStrings: searchStrs) func FindSubString(inputStr : String, subStrings: Array ?) -> 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 = 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 }
возвращается («Почему», 0, 3) («подстроки», 26, 36) («Swift3», 40, 46)
Я создал простое расширение для этого (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..
Swift 4
extension String { subscript(_ i: Int) -> String { let idx1 = index(startIndex, offsetBy: i) let idx2 = index(idx1, offsetBy: 1) return String(self[idx1..
Swift 4
«Подстрока» ( 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!"
Пример расширения Строка:
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).. 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[..
пример использования расширения 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"