Как проверить, содержит ли строка подстроку в Swift? Полное руководство
Привет, коллега-разработчик! Если ты когда-нибудь писал на Swift и задавался вопросом: «Как же мне найти одно слово внутри другого?», — ты попал по адресу. Эта тема — один из самых популярных вопросов на Stack Overflow (английская версия набрала сотни тысяч просмотров). И это не случайно: работа со строками — база, без которой не обходится ни одно приложение.
В этой статье мы разберём все легальные способы проверить, содержит ли строка подстроку. От простейшего contains() до продвинутых техник с регистронезависимым поиском и регулярными выражениями. Поехали!
1. Самый простой способ: метод contains()
Начиная с Swift 1.0, у нас есть встроенный метод contains() у типа String. Он работает интуитивно и очень быстро.
let fullString = "Привет, мир Swift!"
let searchString = "Swift"
if fullString.contains(searchString) {
print("Да, строка содержит '\(searchString)'")
} else {
print("Нет, не содержит")
}
// Вывод: Да, строка содержит 'Swift'
Важно: метод contains() чувствителен к регистру. То есть "swift" не будет найден в строке "Swift". Но мы это исправим чуть позже.
2. Регистронезависимый поиск: localizedCaseInsensitiveContains()
Часто нужно найти слово, не обращая внимания на заглавные буквы. Для этого есть специальный метод, который работает с учётом локали пользователя.
let text = "Добро пожаловать в Swift"
let search = "swift"
if text.localizedCaseInsensitiveContains(search) {
print("Найдено! Регистр не помеха.")
} else {
print("Не найдено")
}
// Вывод: Найдено! Регистр не помеха.
Этот метод использует текущую локаль устройства, что особенно важно для языков с особыми правилами сравнения (например, турецкий, где «i» и «I» — разные буквы).
3. Поиск с учётом локали: localizedStandardContains()
Ещё один полезный метод — localizedStandardContains(). Он выполняет поиск так, как это делает стандартный поиск в iOS (например, в Spotlight или Finder).
let fileName = "Фото_отпуска_2024.jpg"
let query = "отпуска"
if fileName.localizedStandardContains(query) {
print("Файл найден!")
}
// Вывод: Файл найден!
Этот метод автоматически нормализует строки, убирает диакритические знаки (например, «ё» = «е») и работает регистронезависимо. Идеально для поисковых строк в интерфейсе.
4. Классический способ: range(of:)
Иногда нужно не просто узнать, есть ли подстрока, но и получить её позицию. Тут на помощь приходит range(of:). Он возвращает опциональный диапазон (Range<String.Index>?).
let phrase = "Изучаем Swift на pythonlib.ru"
let word = "Swift"
if let range = phrase.range(of: word) {
print("Подстрока найдена на позиции: \(range.lowerBound)")
// Можно даже извлечь кусок строки
let extracted = phrase[range]
print("Извлечено: \(extracted)")
} else {
print("Подстрока не найдена")
}
Для регистронезависимого поиска с range используйте опцию .caseInsensitive:
if let range = phrase.range(of: "swift", options: .caseInsensitive) {
print("Найдено без учёта регистра: \(phrase[range])")
}
5. Проверка начала и конца строки
Иногда нужно проверить не «содержит ли вообще», а «начинается ли с...» или «заканчивается ли на...». Для этого есть специальные методы:
let url = "https://pythonlib.ru"
if url.hasPrefix("https://") {
print("Это безопасный URL")
}
if url.hasSuffix(".ru") {
print("Российский домен")
}
Оба метода тоже чувствительны к регистру. Для регистронезависимой версии придётся привести обе строки к одному регистру вручную:
if url.lowercased().hasPrefix("HTTPS://".lowercased()) {
print("Регистронезависимая проверка сработала")
}
6. Продвинутый поиск: регулярные выражения (NSRegularExpression)
Когда простого поиска недостаточно (например, нужно найти все email'ы или номера телефонов), в дело вступают регулярные выражения. В Swift для этого используется NSRegularExpression.
let text = "Мой email: test@example.com, а также admin@site.org"
let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
do {
let regex = try NSRegularExpression(pattern: pattern, options: .caseInsensitive)
let matches = regex.matches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count))
for match in matches {
if let range = Range(match.range, in: text) {
print("Найден email: \(text[range])")
}
}
} catch {
print("Ошибка в регулярном выражении: \(error.localizedDescription)")
}
Важно: работа с NSRegularExpression требует перехода к NSRange и обратно к Range<String.Index>. Это не очень удобно, но даёт огромную гибкость.
7. Альтернативный подход: NSString bridge
Swift-строки могут быть «мостом» к Objective-C строке NSString. Это даёт доступ к старым, но проверенным методам, таким как contains() с опциями.
let swiftString = "Swift — это мощно!"
let nsString = swiftString as NSString
if nsString.contains("мощно") {
print("Метод NSString тоже работает")
}
// Регистронезависимая версия
if nsString.range(of: "СВИФТ", options: .caseInsensitive).location != NSNotFound {
print("Найдено через NSString с игнором регистра")
}
Однако я рекомендую использовать нативные Swift-методы, если нет необходимости в обратной совместимости.
8. Производительность: что выбрать?
В большинстве случаев contains() — самый быстрый и читаемый вариант. Если нужен регистронезависимый поиск — localizedCaseInsensitiveContains. Для поиска с нормализацией — localizedStandardContains.
Регулярные выражения — мощный, но медленный инструмент. Используйте их только для сложных шаблонов, а не для простого поиска фиксированной подстроки.
9. Практический пример: поиск в списке строк
Часто нужно отфильтровать массив строк по подстроке. Вот как это делается элегантно:
let fruits = ["Яблоко", "Апельсин", "Банан", "Авока