Как найти все позиции одной строки в другой строке в swift2?

Я могу найти первую позицию строки "ATG" в myString "ATGGACGTGAGCTGATCGATGGCTGAAATGAAAA" (то есть индексный диапазон равен 0.. < 3), используя следующий код. Вопрос в том, как найти все позиции "ATG", а не только первую в myString.

let stringRange = myString.rangeOfString("ATG")

Ответ 1

Добро пожаловать в SO.

Это было бы хорошим упражнением по программированию. Я предлагаю вам взять его в качестве учебного проекта.

Напишите функцию, которая берет строку для поиска, и строку для поиска, и возвращает необязательный массив объектов NSRange. Если он не найдет каких-либо вхождений, необязательный параметр будет равен нулю. Кроме того, вы всегда можете вернуть массив, но он содержит 0 объектов NSRange, если строка не найдена.

Попросите вашу функцию использовать метод NSString rangeOfString:options:range: для поиска строки. Сначала вы будете искать всю исходную строку. После того, как вы нашли первое вхождение, вы должны настроить параметр range только для поиска остальной части исходной строки после этого появления.

EDIT:

Элегантный способ сделать это будет как расширение класса String. Таким образом, вы можете использовать свой новый метод, как если бы он был встроенной функцией String s.

Ответ 2

Вы можете использовать NSRegularExpression, чтобы найти все вхождения вашей строки:

Swift 1.2:

let mystr = "ATGGACGTGAGCTGATCGATGGCTGAAATGAAAA"
let searchstr = "ATG"
let ranges: [NSRange]

// Create the regular expression.
if let regex = NSRegularExpression(pattern: searchstr, options: nil, error: nil) {
    // Use the regular expression to get an array of NSTextCheckingResult.
    // Use map to extract the range from each result.
    ranges = regex.matchesInString(mystr, options: nil, range: NSMakeRange(0, count(mystr))).map {$0.range}

} else {
    // There was a problem creating the regular expression
    ranges = []
}

println(ranges)  // prints [(0,3), (18,3), (27,3)]

Swift 2:

let mystr = "ATGGACGTGAGCTGATCGATGGCTGAAATGAAAA"
let searchstr = "ATG"
let ranges: [NSRange]

do {
    // Create the regular expression.
    let regex = try NSRegularExpression(pattern: searchstr, options: [])

    // Use the regular expression to get an array of NSTextCheckingResult.
    // Use map to extract the range from each result.
    ranges = regex.matchesInString(mystr, options: [], range: NSMakeRange(0, mystr.characters.count)).map {$0.range}
}
catch {
    // There was a problem creating the regular expression
    ranges = []
}

print(ranges)  // prints [(0,3), (18,3), (27,3)]

Swift 3: с использованием родного типа Range Swift.

let mystr = "ATGGACGTGAGCTGATCGATGGCTGAAATGAAAA"
let searchstr = "ATG"

do {
    // Create the regular expression.
    let regex = try NSRegularExpression(pattern: searchstr, options: [])

    // Use the regular expression to get an array of NSTextCheckingResult.
    // Use map to extract the range from each result.
    let fullStringRange = mystr.nsRange(from: mystr.startIndex ..< mystr.endIndex)          
    let matches = regex.matches(in: mystr, options: [], range: fullStringRange)
    let ranges = matches.map {$0.range}
    print(ranges)  // prints [(0,3), (18,3), (27,3)]
}
catch {}

Примечания:

  • Этот метод имеет свои ограничения. Вы будете в порядке, если строка, которую вы ищете, - это простой текст, но если строка содержит символы (например, "+*()[].{}?\^$"), которые имеют особое значение в регулярном выражении, это не будет работать должным образом. Вы можете предварительно обработать строку поиска, чтобы добавить escape файлы, чтобы свести к нулю особые значения этих символов, но это, вероятно, больше проблем, чем это стоит.
  • Другое ограничение может быть продемонстрировано, когда mystr составляет "AAAA", а searchstr - "AA". В этом случае строка будет найдена только дважды. Средний AA не будет найден, потому что он начинается с символа, который является частью первого диапазона.

Ответ 3

extension String {
    public func rangesOfString(searchString:String, options: NSStringCompareOptions = [], searchRange:Range<Index>? = nil ) -> [Range<Index>] {
        if let range = rangeOfString(searchString, options: options, range:searchRange) {

            let nextRange = Range(start:range.endIndex, end:self.endIndex)
            return [range] + rangesOfString(searchString, searchRange: nextRange)
        } else {
            return []
        }
    }
}

Ответ 4

Это имеет смысл, потому что в соответствии с документами, rangeOfString:

Находит и возвращает диапазон появления первой данной строки в приемнике.

Если вы хотите найти все вхождения, вы можете зацикливаться до тех пор, пока rangeOfString: не вернет nil и каждый раз обрезает строку до сразу после согласованного диапазона. Конечно, вам нужно будет отслеживать свою позицию в исходной строке и, конечно, переносить индексы.