I can find first position of string \"ATG\" in myString \"ATGGACGTGAGCTGATCGATGGCTGAAATGAAAA\" (i.e. index range is 0..<3) by using code below. Question is how to find al
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 []
}
}
}
That makes sense, because according to the docs, rangeOfString:
Finds and returns the range of the first occurrence of a given string within the receiver.
If you'd like to find all the occurrences, you could loop until rangeOfString:
returns nil, and each time—trim the string up to just after the matched range. You'd have to keep track of your position within the original string and transpose the indexes, of course.
Welcome to SO.
This would be a good programming exercise. I suggest that you take it on as a learning project.
Write a function that takes a string to search in, and a string to search for, and returns an optional array of NSRange objects. If it doesn't find any occurrences, the optional would be nil. Alternately, you could always return an array, but have it contain 0 NSRange objects if the string isn't found.
Have your function use the NSString
method rangeOfString:options:range:
to search the string. First you would search the entire source string. Once you found the first occurrence, you'd adjust the range
parameter to only search the remainder of the source string after that occurrence.
An elegant way to do this would be as an extension to the String
class. That way you could use your new method as if it was a built-in feature of String
s.
You can use NSRegularExpression
to find all occurrences of your string:
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: using Swift's native Range
type.
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 {}
Notes:
"+*()[].{}?\^$"
) which have special meaning in a regular expression, then this will not work as expected. You could preprocess the search string to add escapes to nullify the special meanings of those characters, but this is probably more trouble than it is worth.mystr
is "AAAA"
and searchstr
is "AA"
. In this case, the string will only be found twice. The middle AA
will not be found because it starts with a character that is part of the first range.