Количество вхождений подстроки в строку в Swift
Моя основная строка - "привет Swift Swift и Swift", а подстрока - Swift.
Мне нужно получить количество раз, когда подстрока "Swift" встречается в указанной строке.
Этот код может определить, существует ли шаблон.
var string = "hello Swift Swift and Swift"
if string.rangeOfString("Swift") != nil {
println("exists")
}
Теперь мне нужно знать номер вхождения.
Ответы
Ответ 1
Простой подход состоит в том, чтобы разделить на "Swift"
и вычесть 1 из числа частей:
let s = "hello Swift Swift and Swift"
let tok = s.components(separatedBy:"Swift")
print(tok.count-1)
Этот код печатает 3.
Изменить: До синтаксиса Swift 3 код выглядел следующим образом:
let tok = s.componentsSeparatedByString("Swift")
Ответ 2
Я бы рекомендовал расширение для строки в Swift 3, например:
extension String {
func countInstances(of stringToFind: String) -> Int {
var stringToSearch = self
var count = 0
while let foundRange = stringToSearch.range(of: stringToFind, options: .diacriticInsensitive) {
stringToSearch = stringToSearch.replacingCharacters(in: foundRange, with: "")
count += 1
}
return count
}
}
Это цикл, который находит и удаляет каждый экземпляр stringToFind, увеличивая количество в каждом раунде. Как только searchString больше не содержит stringToFind, цикл прерывается и возвращается счет.
Обратите внимание, что я использую .diacriticInsensitive, поэтому игнорирует акценты (например, résume и resume будут найдены). Вы можете добавить или изменить параметры в зависимости от типов строк, которые вы хотите найти.
Ответ 3
Оптимизация решения dwsolbergs для быстрого подсчета. Также быстрее, чем componentsSeparatedByString
.
extension String {
/// stringToFind must be at least 1 character.
func countInstances(of stringToFind: String) -> Int {
assert(!stringToFind.isEmpty)
var count = 0
var searchRange: Range<String.Index>?
while let foundRange = range(of: stringToFind, options: [], range: searchRange) {
count += 1
searchRange = Range(uncheckedBounds: (lower: foundRange.upperBound, upper: endIndex))
}
return count
}
}
Использование:
// return 2
"aaaa".countInstances(of: "aa")
- Если вы хотите игнорировать акценты, вы можете заменить
options: []
на options: .diacriticInsensitive
, как это сделал dwsolbergs.
- Если вы хотите игнорировать регистр, вы можете заменить
options: []
на options: .caseInsensitive
, как предложили ConfusionTowers.
- Если вы хотите игнорировать как акценты, так и регистр, вы можете заменить
options: []
на options: [.caseInsensitive, .diacriticInsensitive]
, как предложили ConfusionTowers.
- Если, с другой стороны, вы хотите максимально быстрое сравнение и можете гарантировать некоторую каноническую форму для составных последовательностей символов, то вы можете рассмотреть вариант
.literal
, и он будет выполнять только точные совпадения.
Ответ 4
Если вы хотите посчитать символы, а не подстроки:
extension String {
func count(of needle: Character) -> Int {
return reduce(0) {
$1 == needle ? $0 + 1 : $0
}
}
}
Ответ 5
Мне нужен способ подсчета подстрок, который может содержать начало следующей подстроки. Используя расширение dwsolbergs и диапазон строк (из: options: range: locale:), я придумал это расширение строки
extension String
{
/**
Counts the occurrences of a given substring by calling Strings `range(of:options:range:locale:)` method multiple times.
- Parameter substring : The string to search for, optional for convenience
- Parameter allowOverlap : Bool flag indicating whether the matched substrings may overlap. Count of "🐼🐼" in "🐼🐼🐼🐼" is 2 if allowOverlap is **false**, and 3 if it is **true**
- Parameter options : String compare-options to use while counting
- Parameter range : An optional range to limit the search, default is **nil**, meaning search whole string
- Parameter locale : Locale to use while counting
- Returns : The number of occurrences of the substring in this String
*/
public func count(
occurrencesOf substring: String?,
allowOverlap: Bool = false,
options: String.CompareOptions = [],
range searchRange: Range<String.Index>? = nil,
locale: Locale? = nil) -> Int
{
guard let substring = substring, !substring.isEmpty else { return 0 }
var count = 0
let searchRange = searchRange ?? startIndex..<endIndex
var searchStartIndex = searchRange.lowerBound
let searchEndIndex = searchRange.upperBound
while let rangeFound = range(of: substring, options: options, range: searchStartIndex..<searchEndIndex, locale: locale)
{
count += 1
if allowOverlap
{
searchStartIndex = index(rangeFound.lowerBound, offsetBy: 1)
}
else
{
searchStartIndex = rangeFound.upperBound
}
}
return count
}
}
Ответ 6
Попробуйте это
var mainString = "hello Swift Swift and Swift"
var count = 0
mainString.enumerateSubstrings(in: mainString.startIndex..<mainString.endIndex, options: .byWords) { (subString, subStringRange, enclosingRange, stop) in
if case let s? = subString{
if s.caseInsensitiveCompare("swift") == .orderedSame{
count += 1
}
}
}
print(count)
Ответ 7
Мое решение
может быть, будет лучше использовать String.Index вместо диапазона Int, но я думаю, что так будет легче читать.
Строка расширения {
func count(of char: Character, range: (Int, Int)? = nil) -> Int {
let range = range ?? (0, self.count)
return self.enumerated().reduce(0) {
guard ($1.0 >= range.0) && ($1.0 < range.1) else { return $0 }
return ($1.1 == char) ? $0 + 1 : $0
}
}
}