Как работает строковая подстрока в Swift

Я обновлял часть своего старого кода и отвечал на Swift 3, но когда я добрался до Swift Strings и Indexing с подстроками, все стало запутанным.

В частности, я пытался сделать следующее:

let str = "Hello, playground" let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) let prefix = str.substringWithRange(prefixRange) 

где вторая строка давала мне следующую ошибку

Значение типа ‘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 ).. 

Заметки:

  1. I-й и f-й начинаются с 0.

  2. Для f-го я использую offsetBY: f + 1, потому что диапазон использования подписки ... <(полуоткрытый оператор), не включает f-ю позицию.

  3. Конечно, должны включать проверки ошибок, таких как неверный индекс.

Вот функция, которая возвращает подстроку заданной подстроки, когда указаны начальные и конечные индексы. Для полной справки вы можете ознакомиться с приведенными ниже ссылками.

 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" 
  • Java: String split (): Я хочу, чтобы он включал пустые строки в конце
  • Операция XOR с двумя строками в java
  • Строковые литералы: куда они идут?
  • Не являются ли строки Python неизменяемыми? Тогда почему работает + "" + b?
  • Сравнение строки с пустой строкой (Java)
  • Игнорирование акцентированных букв в сравнении строк
  • Форматировать строки в методе Console.WriteLine
  • Как объявить и использовать логические переменные в сценарии оболочки?
  • Как напечатать полное значение длинной строки в gdb?
  • Как обрезать пробелы из строки Python?
  • Как разобрать строку с десятичной точкой в ​​double?
  • Interesting Posts

    Как подключиться к потерянному экрану (отсоединенный, отсутствующий сокет)?

    Как вводить только уникальные значения в столбце Excel 2007

    Как подключить веб-сайт к стартовому экрану Windows 8 с помощью Internet Explorer в качестве браузера по умолчанию?

    Как решить проблему «Bootmgr is missing» на компьютере под управлением Windows 7?

    Похоже, что Firefox и Safari на Mac не могут показать веб-страницу в полноэкранном режиме?

    Обновление Windows 10 от Home to Enterprise Version

    Переseleniumие jacksonа Сериализация и десериализатор

    Невозможно заставить Project Lombok работать над Eclipse (Helios)

    CORS включен, но ответ для предполета имеет недопустимый код статуса HTTP 404, когда POSTING JSON

    Почему я должен использовать push_back вместо emplace_back?

    Автоматическое подключение к удаленной машине

    Как очистить cookies с помощью asp.net mvc 3 и c #?

    Передача дополнительных переменных из командной строки для

    Ошибка при попытке извлечь файлы из zip-файла

    TransformException дубликат записи для common.annotations.Beta

    Давайте будем гением компьютера.