Я использую это в JavaScript:
var domains = "abcde".substring(0, "abcde".indexOf("cd")) // Returns "ab"
У Swift нет этой функции, как сделать что-то подобное?
Я использую это в JavaScript:
var domains = "abcde".substring(0, "abcde".indexOf("cd")) // Returns "ab"
У Swift нет этой функции, как сделать что-то подобное?
изменить/обновить:
Xcode 11 • Swift 5.1 или более поздняя версия
extension StringProtocol {
func index<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
range(of: string, options: options)?.lowerBound
}
func endIndex<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
range(of: string, options: options)?.upperBound
}
func indices<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Index] {
var indices: [Index] = []
var startIndex = self.startIndex
while startIndex < endIndex,
let range = self[startIndex...]
.range(of: string, options: options) {
indices.append(range.lowerBound)
startIndex = range.lowerBound < range.upperBound ? range.upperBound :
index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}
return indices
}
func ranges<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<Index>] {
var result: [Range<Index>] = []
var startIndex = self.startIndex
while startIndex < endIndex,
let range = self[startIndex...]
.range(of: string, options: options) {
result.append(range)
startIndex = range.lowerBound < range.upperBound ? range.upperBound :
index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}
return result
}
}
Использование:
let str = "abcde"
if let index = str.index(of: "cd") {
let substring = str[..<index] // ab
let string = String(substring)
print(string) // "ab\n"
}
let str = "Hello, playground, playground, playground"
str.index(of: "play") // 7
str.endIndex(of: "play") // 11
str.indices(of: "play") // [7, 19, 31]
str.ranges(of: "play") // [{lowerBound 7, upperBound 11}, {lowerBound 19, upperBound 23}, {lowerBound 31, upperBound 35}]
образец без учета регистра
let query = "Play"
let ranges = str.ranges(of: query, options: .caseInsensitive)
let matches = ranges.map { str[$0] } //
print(matches) // ["play", "play", "play"]
образец регулярного выражения
let query = "play"
let escapedQuery = NSRegularExpression.escapedPattern(for: query)
let pattern = "\\b\(escapedQuery)\\w+" // matches any word that starts with "play" prefix
let ranges = str.ranges(of: pattern, options: .regularExpression)
let matches = ranges.map { str[$0] }
print(matches) // ["playground", "playground", "playground"]
Используя индекс String[Range<String.Index>]
, вы можете получить подстроку. Вам нужен начальный индекс и последний индекс, чтобы создать диапазон, и вы можете сделать это, как показано ниже
let str = "abcde"
if let range = str.range(of: "cd") {
let substring = str[..<range.lowerBound] // or str[str.startIndex..<range.lowerBound]
print(substring) // Prints ab
}
else {
print("String not present")
}
Если вы не определяете начальный индекс этого оператора ..<
, он принимает начальный индекс. Вы также можете использовать str[str.startIndex..<range.lowerBound]
вместо str[..<range.lowerBound]
let str = "abcdefghabcd"
if let index = str.index(of: "b") {
print(index) // Index(_compoundOffset: 4, _cache: Swift.String.Index._Cache.character(1))
}
let str : String = "ilike"
for i in 0...str.count {
let index = str.index(str.startIndex, offsetBy: i) // String.Index
let prefix = str[..<index] // String.SubSequence
let suffix = str[index...] // String.SubSequence
print("prefix \(prefix), suffix : \(suffix)")
}
prefix , suffix : ilike
prefix i, suffix : like
prefix il, suffix : ike
prefix ili, suffix : ke
prefix ilik, suffix : e
prefix ilike, suffix :
let substring1 = string[startIndex...endIndex] // including endIndex
let subString2 = string[startIndex..<endIndex] // excluding endIndex
Выполнение этого в Swift возможно, но оно требует больше строк, вот функция indexOf()
делает то, что ожидается:
func indexOf(source: String, substring: String) -> Int? {
let maxIndex = source.characters.count - substring.characters.count
for index in 0...maxIndex {
let rangeSubstring = source.startIndex.advancedBy(index)..<source.startIndex.advancedBy(index + substring.characters.count)
if source.substringWithRange(rangeSubstring) == substring {
return index
}
}
return nil
}
var str = "abcde"
if let indexOfCD = indexOf(str, substring: "cd") {
let distance = str.startIndex.advancedBy(indexOfCD)
print(str.substringToIndex(distance)) // Returns "ab"
}
Эта функция не оптимизирована, но выполняет задание для коротких строк.
В версии Swift версии 3 у String нет таких функций, как -
str.index(of: String)
Если для подстроки требуется индекс, один из способов - получить диапазон. У нас есть следующие функции в строке, которая возвращает диапазон -
str.range(of: <String>)
str.rangeOfCharacter(from: <CharacterSet>)
str.range(of: <String>, options: <String.CompareOptions>, range: <Range<String.Index>?>, locale: <Locale?>)
Например, чтобы найти индексы первого появления воспроизведения в str
var str = "play play play"
var range = str.range(of: "play")
range?.lowerBound //Result : 0
range?.upperBound //Result : 4
Примечание: диапазон является необязательным. Если он не сможет найти String, он сделает его нулевым. Например
var str = "play play play"
var range = str.range(of: "zoo") //Result : nil
range?.lowerBound //Result : nil
range?.upperBound //Result : nil
Здесь есть три тесно связанных вопроса:
Все методы поиска подстроки закончились в мире Cocoa NSString (Фонд)
Фонд NSRange имеет несоответствие с Swift Range; первый использует начало и длину, последний использует конечные точки
В общем, символы Swift индексируются с использованием String.Index
, а не Int, но символы Foundation индексируются с использованием Int, и между ними нет простого прямого перевода (поскольку Foundation и Swift имеют разные представления о том, что составляет символ)
Учитывая все это, давайте подумаем, как написать:
func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? {
// ?
}
Подстрока s2
должна быть найдена в s
использованием метода String Foundation. Полученный диапазон возвращается к нам не как NSRange (даже если это метод Foundation), а как Range из String.Index
(заключенный в необязательный String.Index
, если мы вообще не нашли подстроку). Тем не менее, другой номер, from
, является Int. Таким образом, мы не можем сформировать какой-либо диапазон, включающий их обоих.
Но мы не должны! Все, что нам нужно сделать, это отрезать конец нашей исходной строки, используя метод, который принимает String.Index
, и отрезать начало нашей исходной строки, используя метод, который принимает Int. К счастью, такие методы существуют! Как это:
func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? {
guard let r = s.range(of:s2) else {return nil}
var s = s.prefix(upTo:r.lowerBound)
s = s.dropFirst(from)
return s
}
Или, если вы предпочитаете иметь возможность применять этот метод непосредственно к строке, как это...
let output = "abcde".substring(from:0, toSubstring:"cd")
... тогда сделайте расширение на String:
extension String {
func substring(from:Int, toSubstring s2 : String) -> Substring? {
guard let r = self.range(of:s2) else {return nil}
var s = self.prefix(upTo:r.lowerBound)
s = s.dropFirst(from)
return s
}
}
Рассматривали ли вы использовать NSRange?
if let range = mainString.range(of: mySubString) {
//...
}
Лео Дабус ответ велик. Вот мой ответ, основанный на его ответе с использованием compactMap
, чтобы избежать ошибки Index out of range
.
extension StringProtocol {
func ranges(of targetString: Self, options: String.CompareOptions = [], locale: Locale? = nil) -> [Range<String.Index>] {
let result: [Range<String.Index>] = self.indices.compactMap { startIndex in
let targetStringEndIndex = index(startIndex, offsetBy: targetString.count, limitedBy: endIndex) ?? endIndex
return range(of: targetString, options: options, range: startIndex..<targetStringEndIndex, locale: locale)
}
return result
}
}
// Usage
let str = "Hello, playground, playground, playground"
let ranges = str.ranges(of: "play")
ranges.forEach {
print("[\($0.lowerBound.utf16Offset(in: str)), \($0.upperBound.utf16Offset(in: str))]")
}
// result - [7, 11], [19, 23], [31, 35]
Swift 5
Найти индекс подстроки
let str = "abcdecd"
if let range: Range<String.Index> = str.range(of: "cd") {
let index: Int = str.distance(from: str.startIndex, to: range.lowerBound)
print("index: ", index) //index: 2
}
else {
print("substring not found")
}
Найти индекс персонажа
let str = "abcdecd"
if let firstIndex = str.firstIndex(of: "c") {
let index = str.distance(from: str.startIndex, to: firstIndex)
print("index: ", index) //index: 2
}
else {
print("symbol not found")
}