Как строки подстроку работы в Swift
я обновлял некоторые из моих старых кодов и ответов с помощью Swift 3, но когда я добрался до Swift Strings и индексирования с подстроками, все стало запутанным.
в частности, я пытался следующее:
let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5)
let prefix = str.substringWithRange(prefixRange)
где вторая строка дает мне следующую ошибку
значение типа 'String' не имеет члена 'substringWithRange'
Я вижу String
имеет следующие методы теперь:
str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)
Они действительно сбивали меня с толку сначала, поэтому я начал играть вокруг индекса и диапазона. Это следующий вопрос и ответ для подстроки. Я добавляю ответ ниже, чтобы показать, как они используются.
11 ответов:
во всех приведенных ниже примерах используется
var str = "Hello, playground"
Swift 4
строки получили довольно большой ремонт в Swift 4. Когда вы получаете некоторую подстроку из строки сейчас, вы получаете
Substring
тип, а неString
. Почему это так? Строки-это типы значений в Swift. Это означает, что если вы используете одну строку, чтобы сделать новую, то она должна быть скопирована. Это хорошо для стабильности (никто другой не собирается менять его без ваши знания), но плохо для эффективности.подстрока, с другой стороны, является ссылкой на исходную строку, из которой она пришла. Вот изображение из документация свидетельствует о том, что.
копирование не требуется, поэтому его гораздо эффективнее использовать. Однако, представьте, у вас есть десять символов подстроки из строки символов. Поскольку подстрока ссылается на строку, система должна была бы держитесь за всю строку до тех пор, пока подстрока вокруг. Таким образом, всякий раз, когда вы закончите манипулировать своей подстрокой, преобразуйте ее в строку.
let myString = String(mySubstring)
это скопирует только подстроку, и старая строка может быть собрана мусор. Подстроки (как тип) должны быть недолговечными.
еще одним большим улучшением в Swift 4 является то, что строки являются коллекциями (опять же). Это означает, что все, что вы можете сделать с коллекцией, вы можете сделать со строкой (использовать индексы, итерация по символам, фильтр и т. д.).
в следующих примерах показано, как получить подстроку в Swift.
получение подстроки
вы можете получить подстроку из строки с помощью индексов или ряд других методов (например,
prefix
,suffix
,split
). Вам все равно нужно использоватьString.Index
, а неInt
индекс для диапазона, хотя. (См.мой другой ответ если вам нужна помощь с что.)начало строки
вы можете использовать индекс (обратите внимание на односторонний диапазон Swift 4):
let index = str.index(str.startIndex, offsetBy: 5) let mySubstring = str[..<index] // Hello
или
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
символы строки.я не решаюсь использовать
Int
расширение индекса на основе после прочтения статьи строки в Swift 3 по скорости полета и Ole Begemann. Хотя в Swift 4 строки являются коллекциями, команда Swift намеренно не использовалаInt
индексы. Это все ещеString.Index
. Это связано с тем, что Swift-символы состоят из различного количества кодовых точек Unicode. Фактический индекс должен быть однозначно рассчитан для каждой строки.я должен сказать, я надеюсь, что команда Swift найдет способ абстрагироваться
String.Index
в будущем. Но до тех пор, пока я не решу использовать их API. Это помогает мне помнить, что строковые манипуляции не простоInt
поиск по индексу.
Я действительно разочарован в модели доступа к строкам 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<Int>) -> String { let startIndex = index(from: r.lowerBound) let endIndex = index(from: r.upperBound) return substring(with: startIndex..<endIndex) } } let str = "Hello, playground" print(str.substring(from: 7)) // playground print(str.substring(to: 5)) // Hello print(str.substring(with: 7..<11)) // play
Swift 4 Расширение:
extension String { subscript(_ range: CountableRange<Int>) -> String { let idx1 = index(startIndex, offsetBy: max(0, range.lowerBound)) let idx2 = index(startIndex, offsetBy: min(self.count, range.upperBound)) return String(self[idx1..<idx2]) } }
использование:
let s = "hello" s[0..<3] // "hel" s[3..<s.count] // "lo"
или unicode:
let s = "" s[0..<1] // ""
Swift 4
в swift 4
String
соответствует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..<end] // The result is of type Substring
интересно знать, что это даст вам
Substring
вместоString
. Это быстро и эффективно, какSubstring
разделяет свое хранилище с исходной строкой. Однако совместное использование памяти также может легко привести к памяти подтеки.вот почему вы должны скопировать результат в новую строку, когда вы хотите очистить исходную строку. Вы можете сделать это с помощью обычного конструктора:
let newString = String(result)
вы можете найти дополнительную информацию о новом
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..<end] return String(subString) } }
у меня была та же реакция. Я тоже был разочарован тем, как синтаксис и объекты меняются так резко в каждом крупном выпуске.
поэтому я решил признать и уважать усилия, прилагаемые инженерами Apple, и внести свой вклад, понимая их мышление, когда они придумали этот "ужасный" подход.
вместо того, чтобы создавать расширения, которые просто обходной путь, чтобы сделать вашу жизнь проще (я не говорю, что они неправильные или дорогие), почему бы не выяснить, как строки теперь предназначены для работы.
например, у меня был этот код, который работал на 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 )..<s.index(s.startIndex, offsetBy:f+1 )]) //print cdefg
для a подстрока (диапазон) из строки с помощью строки.подстрока (диапазон) мы можем получить подстроку с помощью кода ниже:
print (s.substring (with:s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 ) ) ) //print cdefg
Примечания:
i-й и f-й начинаются с 0.
для f-го я использую offsetBY: f + 1, потому что диапазон использования подписки ..
конечно, должны включать в себя проверить ошибки, такие как недопустимый индекс.
вот функция, которая возвращает подстроку данной подстроки, когда предоставляются начальный и конечный индексы. Для полной справки вы можете посетить ссылки, приведенные ниже.
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..<endIndex]) }else{ return nil } }
вот ссылка на сообщение в блоге, которое я создал для работы со строковыми манипуляциями в swift. строка манипуляции в swift (охватывает swift 4, а также)
такое же разочарование, это не должно быть так сложно...
я скомпилировал этот пример получения позиций для подстроки(ов) из большего текста:
// // 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<String>? = ["Why", "substrings", "Swift3"] FindSubString(inputStr: Bigstring, subStrings: searchStrs) func FindSubString(inputStr : String, subStrings: Array<String>?) -> 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<String.Index> = 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..<end) } }
Swift 4
extension String { subscript(_ i: Int) -> String { let idx1 = index(startIndex, offsetBy: i) let idx2 = index(idx1, offsetBy: 1) return String(self[idx1..<idx2]) } } let s = "hello" s[0] // h s[1] // e s[2] // l s[3] // l s[4] // o
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)..<result.index(result.endIndex, offsetBy: -1)]) } return result } else if let _from: Character = from { guard let startOfSentence: String.Index = self.index(of: _from) else { return nil } let result: String = String(self[startOfSentence...]) if include == false { guard result.count > 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[..<result.index(result.endIndex, offsetBy: -1)]) } return result } return nil } }
пример использования строки расширения:
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"