Как проверить, содержит ли строка другую строку в Swift?
Основное решение: метод contains(_:)
В Swift (начиная с версии 3.0) для проверки, содержит ли строка другую строку, используется метод contains(_:). Он возвращает true, если подстрока найдена, и false в противном случае. Вот как исправить ваш код:
let string = "hello Swift"
if string.contains("Swift") {
print("Строка содержит 'Swift'")
} else {
print("Строка не содержит 'Swift'")
}
// Вывод: Строка содержит 'Swift'
Метод contains(_:) является частью протокола StringProtocol, поэтому он работает как с String, так и с Substring. Он чувствителен к регистру по умолчанию.
Проверка без учёта регистра (case-insensitive)
Если вам нужно найти подстроку независимо от регистра, используйте метод localizedCaseInsensitiveContains(_:) или преобразуйте обе строки к одному регистру:
let string = "Hello Swift"
let substring = "swift"
// Вариант 1: использование встроенного метода
if string.localizedCaseInsensitiveContains(substring) {
print("Строка содержит 'swift' (без учёта регистра)")
}
// Вариант 2: приведение к нижнему регистру
if string.lowercased().contains(substring.lowercased()) {
print("То же самое, но через lowercased()")
}
// Вывод: Строка содержит 'swift' (без учёта регистра)
Метод localizedCaseInsensitiveContains(_:) учитывает локализацию (например, турецкую букву 'i'), в отличие от простого lowercased(). Для большинства случаев используйте его.
Поиск с указанием позиции (range)
Если вам нужно не просто проверить наличие, но и узнать, где именно находится подстрока, используйте метод range(of:). Он возвращает опциональный Range<String.Index>:
let string = "hello Swift, hello world"
if let range = string.range(of: "hello") {
print("Первое вхождение 'hello' найдено на позиции: \(range.lowerBound)")
// range.lowerBound — это String.Index, можно преобразовать в Int
let startIndex = string.distance(from: string.startIndex, to: range.lowerBound)
print("Числовой индекс: \(startIndex)")
} else {
print("Подстрока не найдена")
}
// Вывод: Первое вхождение 'hello' найдено на позиции: Index(_rawBits: 0)
// Числовой индекс: 0
Чтобы найти все вхождения, используйте цикл:
let string = "hello Swift, hello world"
let searchString = "hello"
var searchRange = string.startIndex..<string.endIndex
while let range = string.range(of: searchString, range: searchRange) {
let startIndex = string.distance(from: string.startIndex, to: range.lowerBound)
print("Найдено '\(searchString)' на позиции: \(startIndex)")
searchRange = range.upperBound..<string.endIndex
}
// Вывод:
// Найдено 'hello' на позиции: 0
// Найдено 'hello' на позиции: 13
Поиск с учётом регистра и локализованное сравнение
Метод range(of:) также поддерживает опции сравнения:
let string = "Hello Swift"
let substring = "swift"
// Без учёта регистра
if let range = string.range(of: substring, options: .caseInsensitive) {
print("Найдено '\(substring)' без учёта регистра")
}
// Локализованное сравнение (например, для немецкого 'ß' -> 'ss')
if let range = string.range(of: "swift", options: [.caseInsensitive, .diacriticInsensitive]) {
print("Найдено с игнорированием диакритических знаков")
}
// Обратный поиск (с конца строки)
if let range = string.range(of: "o", options: .backwards) {
let index = string.distance(from: string.startIndex, to: range.lowerBound)
print("Последнее вхождение 'o' на позиции: \(index)")
}
// Вывод: Последнее вхождение 'o' на позиции: 4
Проверка префикса и суффикса
Для частных случаев проверки начала или конца строки используйте:
let string = "Hello Swift"
if string.hasPrefix("Hello") {
print("Строка начинается с 'Hello'")
}
if string.hasSuffix("Swift") {
print("Строка заканчивается на 'Swift'")
}
// Вывод: оба условия истинны
Рекомендации
- Для простой проверки используйте
contains(_:)— он самый быстрый и читаемый. - Для поиска без учёта регистра —
localizedCaseInsensitiveContains(_:). - Для получения позиции подстроки —
range(of:)с опциями. - Избегайте использования Objective-C методов (
rangeOfString) в чистом Swift коде — они работают, но менее эффективны и не идиоматичны.
Надеюсь, это поможет! Помните, что строки в Swift — это не просто массивы символов, а полноценные коллекции с Unicode-поддержкой, поэтому индексы работают не как целые числа, а как специальные String.Index.
Альтернативный подход: использование NSString моста
Если вы переходите с Objective-C и чувствуете себя неуверенно с новым API, вы можете использовать мост между String и NSString. Swift автоматически мостит строки, поэтому вы можете вызывать методы NSString напрямую:
let string = "hello Swift" as NSString
if string.range(of: "Swift").location != NSNotFound {
print("Строка содержит 'Swift' (через NSString)")
}
// Вывод: Строка содержит 'Swift' (через NSString)
Однако этот подход не рекомендуется для нового кода, так как:
- Он менее производительный из-за моста.
- Не использует преимущества Swift (типобезопасность, Unicode-корректность).
- Методы
NSStringработают сNSRange, а не сRange<String.Index>, что может вызвать путаницу.
Использование регулярных выражений
Для сложных шаблонов поиска используйте NSRegularExpression:
import Foundation
let string = "Hello Swift 5.0"
let pattern = "Swift\\s\\d+\\.\\d+" // поиск "Swift" + версия
if let regex = try? NSRegularExpression(pattern: pattern) {
let range = NSRange(string.startIndex..., in: string)
if regex.firstMatch(in: string, range: range) != nil {
print("Найдено совпадение с шаблоном")
}
}
// Вывод: Найдено совпадение с шаблоном
Это мощный инструмент, но для простых проверок он избыточен. Используйте его только когда contains(_:) или range(of:) недостаточно.
Проверка на пустую строку
Если вы ищете пустую строку, contains("") всегда вернёт true, так как пустая строка считается подстрокой любой строки. Учитывайте это в логике:
let string = "hello"
let empty = ""
if string.contains(empty) {
print("Любая строка содержит пустую подстроку") // всегда true
}
В целом, придерживайтесь нативных методов Swift