Как работает строковая подстрока в Swift
я обновлял некоторые из моих старых кодов и ответов с помощью Swift 3, но когда я добрался до строк Swift и индексирования с подстроками, все запуталось.
в частности, я пробовал следующее:
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
Strings получил довольно большой ремонт в 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 подстрока (диапазон) из строки с помощью String.подстрока (диапазон), мы можем получить подстроку, используя код ниже:
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"