Регулярные выражения в Swift: мощный инструмент для работы с текстом
Регулярные выражения (regex) — это незаменимый инструмент для разработчиков iOS и macOS. Они позволяют выполнять сложный поиск, замену и валидацию текстовых данных с помощью компактных шаблонов. В Swift работа с регулярными выражениями реализована через класс NSRegularExpression из Foundation framework. В этой статье мы разберем все аспекты использования regex в Swift: от базового синтаксиса до продвинутых техник, с практическими примерами для iOS и macOS приложений.
Основы NSRegularExpression в Swift
Для работы с регулярными выражениями в Swift используется класс NSRegularExpression. Он предоставляет мощные методы для поиска, замены и извлечения данных из строк. Рассмотрим базовый синтаксис инициализации:
import Foundation
let pattern = "\\d{3}-\\d{2}-\\d{4}" // Шаблон для SSN
let text = "Мой SSN: 123-45-6789"
do {
let regex = try NSRegularExpression(pattern: pattern)
let results = regex.matches(in: text, range: NSRange(text.startIndex..., in: text))
for match in results {
let range = Range(match.range, in: text)!
print("Найдено: \(text[range])")
}
} catch {
print("Ошибка в регулярном выражении: \(error.localizedDescription)")
}
Обратите внимание на экранирование обратной косой черты в Swift-строках. Для каждого обратного слеша в шаблоне нужно ставить два (\\d вместо \d).
Создание и настройка регулярного выражения
Конструктор NSRegularExpression принимает опциональный параметр options, который позволяет настроить поведение regex:
.caseInsensitive— игнорировать регистр символов.dotMatchesLineSeparators— точка соответствует символам перевода строки.anchorsMatchLines— якоря ^ и $ работают для каждой строки.useUnixLineSeparators— использовать только \n как разделитель строк
let regex = try NSRegularExpression(
pattern: "swift",
options: [.caseInsensitive]
)
// Теперь шаблон найдет "Swift", "SWIFT", "swift" и т.д.
Поиск и извлечение данных с помощью regex
Одна из главных задач регулярных выражений — поиск подстрок, соответствующих шаблону. В Swift это делается через метод matches(in:options:range:).
Поиск всех совпадений
let text = "Контакты: email@example.com, support@test.org, admin@site.com"
let emailPattern = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
let regex = try NSRegularExpression(pattern: emailPattern)
let matches = regex.matches(in: text, range: NSRange(text.startIndex..., in: text))
print("Найдено email: \(matches.count)")
for match in matches {
if let range = Range(match.range, in: text) {
print(String(text[range]))
}
}
// Вывод:
// Найдено email: 3
// email@example.com
// support@test.org
// admin@site.com
Поиск с захватом групп
Группы в круглых скобках позволяют извлекать части совпадения. Это особенно полезно для парсинга структурированных данных:
let logLine = "2024-01-15 14:30:22 [ERROR] Не удалось подключиться к серверу"
let logPattern = "(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}) \\[(\\w+)\\] (.+)"
let regex = try NSRegularExpression(pattern: logPattern)
if let match = regex.firstMatch(in: logLine, range: NSRange(logLine.startIndex..., in: logLine)) {
let dateRange = Range(match.range(at: 1), in: logLine)!
let timeRange = Range(match.range(at: 2), in: logLine)!
let levelRange = Range(match.range(at: 3), in: logLine)!
let messageRange = Range(match.range(at: 4), in: logLine)!
print("Дата: \(logLine[dateRange])")
print("Время: \(logLine[timeRange])")
print("Уровень: \(logLine[levelRange])")
print("Сообщение: \(logLine[messageRange])")
}
Замена текста с помощью регулярных выражений
Метод stringByReplacingMatches(in:options:range:withTemplate:) позволяет выполнять замену по шаблону. Это мощный инструмент для очистки и трансформации текста.
Простая замена
let originalText = "Мой номер телефона: 8-800-555-35-35"
let phonePattern = "\\d-\\d{3}-\\d{3}-\\d{2}-\\d{2}"
let replacement = "[ТЕЛЕФОН СКРЫТ]"
let regex = try NSRegularExpression(pattern: phonePattern)
let maskedText = regex.stringByReplacingMatches(
in: originalText,
range: NSRange(originalText.startIndex..., in: originalText),
withTemplate: replacement
)
print(maskedText) // "Мой номер телефона: [ТЕЛЕФОН СКРЫТ]"
Замена с использованием групп
Шаблоны замены могут ссылаться на захваченные группы с помощью $1, $2 и т.д.:
let text = "Иванов Иван, Петров Петр, Сидоров Алексей"
let namePattern = "(\\w+) (\\w+)"
let replacementTemplate = "$2 $1" // Меняем местами фамилию и имя
let regex = try NSRegularExpression(pattern: namePattern)
let result = regex.stringByReplacingMatches(
in: text,
range: NSRange(text.startIndex..., in: text),
withTemplate: replacementTemplate
)
print(result) // "Иван Иванов, Петр Петров, Алексей Сидоров"
Валидация данных с помощью regex
Регулярные выражения идеально подходят для проверки корректности вводимых данных. Вот несколько практических примеров для iOS-приложений:
Валидация email
func isValidEmail(_ email: String) -> Bool {
let emailPattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let predicate = NSPredicate(format: "SELF MATCHES %@", emailPattern)
return predicate.evaluate(with: email)
}
print(isValidEmail("user@example.com")) // true
print(isValidEmail("invalid