Как правильно работать с подстрокой (substring) в Swift?
Почему в Swift так сложно с подстроками?
Swift уделяет большое внимание безопасности и Unicode-корректности. Строки в Swift состоят не из байтов или простых "символов", а из графем-кластеров (grapheme clusters). Это значит, что один видимый символ (например, "é" или "👍🏽") может состоять из нескольких Unicode-кодовых точек. Поэтому просто взять и обратиться по индексу [0] — некорректно с точки зрения Unicode, так как индекс должен учитывать эти кластеры. Именно поэтому Swift использует String.Index, а не целые числа.
Правильный способ: работа с индексами
Чтобы получить подстроку, нужно сначала создать диапазон (range) индексов, а затем использовать сабскрипт. Вот базовый пример:
let text = "Hello, world!"
// Получаем первые 5 символов
let startIndex = text.startIndex
let endIndex = text.index(startIndex, offsetBy: 5)
let substring = text[startIndex..<endIndex] // "Hello"
print(substring) // Выведет: Hello
Обратите внимание: text[startIndex..<endIndex] возвращает Substring, а не String. Substring — это легковесное представление, которое ссылается на память исходной строки. Чтобы получить полноценный String, просто инициализируйте новую строку:
let newString = String(text[startIndex..<endIndex]) // "Hello"
Практические примеры для ваших задач
Вот как решить три ваши задачи:
1. Первые 5 символов
let text = "Hello, world!"
let prefix = String(text.prefix(5)) // "Hello"
// Или через индексы:
let range = text.startIndex..<text.index(text.startIndex, offsetBy: 5)
let prefix2 = String(text[range])
2. Последние 3 символа
let suffix = String(text.suffix(3)) // "ld!"
// Через индексы:
let start = text.index(text.endIndex, offsetBy: -3)
let range = start..<text.endIndex
let suffix2 = String(text[range])
3. Часть с 3-го по 7-й символ (индексы с 0)
let start = text.index(text.startIndex, offsetBy: 2) // 3-й символ (индекс 2)
let end = text.index(text.startIndex, offsetBy: 7) // 8-й символ (индекс 7)
let range = start..<end
let middle = String(text[range]) // "llo, "
Удобные методы-обёртки
Чтобы не писать каждый раз индексы, можно создать свои функции или расширения:
extension String {
func substring(from: Int, to: Int) -> String {
let start = index(startIndex, offsetBy: from)
let end = index(startIndex, offsetBy: to)
return String(self[start..<end])
}
func substring(from: Int) -> String {
let start = index(startIndex, offsetBy: from)
return String(self[start...])
}
func substring(to: Int) -> String {
let end = index(startIndex, offsetBy: to)
return String(self[..<end])
}
}
// Использование:
let text = "Hello, world!"
print(text.substring(from: 0, to: 5)) // "Hello"
print(text.substring(from: 7)) // "world!"
print(text.substring(to: 5)) // "Hello"
Важно: Substring vs String
- Substring — это временное представление, которое ссылается на память исходной строки. Это эффективно, так как не требует копирования.
- String — это независимая копия. Если вы планируете хранить подстроку долго или передавать её в другой контекст, всегда конвертируйте Substring в String.
- Substring поддерживает те же методы, что и String (например,
.count,.uppercased()), но его нельзя использовать там, где ожидается String (например, в параметрах функции).
Надеюсь, теперь стало понятнее. Swift жертвует краткостью кода ради безопасности и корректной работы с Unicode. Как только вы привыкнете к индексам, это перестанет казаться сложным.
<<Альтернативный подход: использование NSString
Если вам очень неудобно работать с String.Index, вы можете временно преобразовать строку в NSString из Objective-C, который поддерживает целочисленные индексы. Это особенно удобно, если вы работаете с ASCII-текстом или английскими строками без специальных символов.
import Foundation
let text = "Hello, world!" as NSString
let sub1 = text.substring(with: NSRange(location: 0, length: 5)) // "Hello"
let sub2 = text.substring(from: 7) // "world!"
let sub3 = text.substring(to: 5) // "Hello"
// Результат — String, не Substring
print(type(of: sub1)) // String
Однако у этого подхода есть недостатки:
- Работает корректно только с символами, которые умещаются в один UTF-16 код (большинство латиницы, кириллицы, цифры). Для эмодзи или составных символов (например, "é" как комбинация "e" + accent) может дать неверный результат.
- Требует импорта Foundation.
- Не является "чистым Swift" — это мост к Objective-C.
Когда использовать?
Если вы точно знаете, что строка содержит только простые символы (например, номера телефонов, ID, английские слова), и вам нужно быстро прототипировать — можно использовать NSString. Для production-кода, особенно с поддержкой мультиязычности, лучше освоить родной Swift-подход с индексами.
Также помните: если вы используете NSString, то длина строки считается в UTF-16 единицах, а не в графем-кластерах. Для большинства европейских языков разницы нет, но для эмодзи и азиатских иероглифов она критична.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги