Как проверить, содержит ли строка другую строку в Swift?
Основное решение: метод `contains(_:)`
В Swift, начиная с версии 4.0, у типа String есть встроенный метод contains(_:). Ошибка, которую вы получили, скорее всего, связана с тем, что вы используете устаревшую версию Swift (до 4.0) или пытаетесь вызвать метод на NSString без явного приведения. Вот правильный синтаксис для современных версий Swift:
let name = "Johnny Appleseed"
let searchText = "john"
if name.contains(searchText) {
print("Строка '\(name)' содержит '\(searchText)'")
} else {
print("Строка '\(name)' НЕ содержит '\(searchText)'")
}
// Вывод: Строка 'Johnny Appleseed' НЕ содержит 'john' (из-за регистра)
Регистронезависимая проверка
Метод contains(_:) по умолчанию чувствителен к регистру. Чтобы сделать проверку регистронезависимой, приведите обе строки к одному регистру (например, к нижнему) с помощью lowercased():
if name.lowercased().contains(searchText.lowercased()) {
print("Найдено! (регистронезависимо)")
} else {
print("Не найдено")
}
// Вывод: Найдено! (регистронезависимо)
Альтернативный подход: метод `range(of:options:)`
Если вам нужен более тонкий контроль (например, поиск с учётом локали или поиск в начале/конце строки), используйте метод range(of:options:). Это особенно полезно для регистронезависимого поиска с учётом правил локали (например, буквы "ä" и "ae" в немецком):
let name = "Johnny Appleseed"
let searchText = "john"
if let range = name.range(of: searchText, options: .caseInsensitive) {
print("Найдено в диапазоне: \(range)")
// Вывод: Найдено в диапазоне: 0..<4
} else {
print("Не найдено")
}
Обратите внимание: options: .caseInsensitive — это регистронезависимый поиск. Также можно комбинировать опции, например [.caseInsensitive, .diacriticInsensitive] для игнорирования диакритических знаков (é = e).
Получение индекса найденной подстроки
Если вам нужно не просто узнать, есть ли подстрока, но и получить её позицию (например, чтобы выделить текст жирным), используйте range(of:) и извлеките lowerBound:
if let range = name.range(of: searchText, options: .caseInsensitive) {
let startIndex = range.lowerBound
let endIndex = range.upperBound
print("Подстрока с индекса \(startIndex) по \(endIndex)")
// Можно получить саму подстроку:
let foundString = name[range]
print("Найденный фрагмент: \(foundString)") // "John"
}
Практический пример для поиска контактов
Вот как можно реализовать фильтрацию массива контактов с учётом регистра:
let contacts = ["Johnny Appleseed", "Jane Doe", "John Smith", "Alice Johnson"]
let searchText = "john"
let filteredContacts = contacts.filter { contact in
contact.lowercased().contains(searchText.lowercased())
}
print(filteredContacts) // ["Johnny Appleseed", "John Smith", "Alice Johnson"]
Важные замечания
- Производительность: Для больших массивов (10 000+ элементов) метод
lowercased()создаёт новые строки, что может быть затратно. В таких случаях используйтеrange(of:options:)с опцией.caseInsensitive— это эффективнее. - Unicode: Swift отлично работает с Unicode. Строки типа "café" и "cafe\u{0301}" (e + комбинирующий акцент) будут считаться разными. Для поиска без учёта диакритики добавьте
.diacriticInsensitive. - Пустая строка: Любая строка содержит пустую подстроку. Помните об этом при валидации ввода пользователя.
Рекомендую всегда использовать contains(_:) для простых проверок и range(of:options:) для более сложных сценариев. Это современный и идиоматичный Swift!
Альтернативный подход с использованием `NSString` и `localizedStandardContains`
Если вы работаете с текстом, введённым пользователем, и хотите получить поведение, похожее на поиск в системных приложениях Apple (например, в Контактах или Почте), используйте метод localizedStandardContains. Этот метод автоматически выполняет регистронезависимый и диакритически нечувствительный поиск, а также учитывает текущую локаль пользователя:
import Foundation
let name = "Johnny Appleseed"
let searchText = "john"
if name.localizedStandardContains(searchText) {
print("Найдено с учётом локали!")
} else {
print("Не найдено")
}
// Вывод: Найдено с учётом локали!
Почему это удобно?
- Не нужно вручную вызывать
lowercased()или указывать опции. - Работает корректно с буквами разных алфавитов (например, "ß" = "ss" в немецком).
- Этот метод используется в системном поиске iOS/macOS, поэтому пользователи ожидают именно такого поведения.
Пример с массивом контактов
let contacts = ["Johnny Appleseed", "José Garcia", "JOHN SMITH"]
let searchText = "jos"
let filtered = contacts.filter { $0.localizedStandardContains(searchText) }
print(filtered) // ["José Garcia", "JOHN SMITH"]
Обратите внимание: localizedStandardContains доступен только для строк (Swift String), не для NSString. Этот метод появился в Swift 3 и является рекомендуемым способом для пользовательского поиска.
Ответов пока нет.
Войдите чтобы оставить ответ