$ sudo teach IT

Зачем строкам столько методов

В реальных программах со строками почти никогда не работают посимвольно. Обычно нужно спросить: «начинается ли имя файла с этого префикса», «есть ли в тексте слово-стоп-фраза», «уберём лишние пробелы, которые пользователь случайно вставил в поле ввода», «разобьём фразу на слова и снова соберём её по-другому». Для всего этого в String уже есть готовые методы — их не нужно писать вручную через индексы и циклы.

Поиск и проверка: contains, hasPrefix, hasSuffix

contains отвечает на вопрос «есть ли такой кусок текста где-нибудь внутри», а hasPrefix и hasSuffix — «начинается» и «заканчивается» ли строка на заданный фрагмент. Все три метода возвращают Bool.

let fileName = "report_final.pdf"

print(fileName.hasPrefix("report"))   // true
print(fileName.hasSuffix(".pdf"))     // true
print(fileName.contains("final"))     // true
print(fileName.contains("draft"))     // false

Обратите внимание: все три метода чувствительны к регистру. "Report".hasPrefix("report") вернёт false, потому что заглавная R и строчная r — разные символы.

Замена: replacingOccurrences(of:with:)

Когда нужно не найти фрагмент, а заменить его на другой, используют replacingOccurrences(of:with:). Метод заменяет все вхождения искомого текста и возвращает новую строку — исходная строка при этом не меняется, потому что String в Swift — тип значения.

let message = "Привет, гость! Гость, добро пожаловать."
let fixed = message.replacingOccurrences(of: "Гость", with: "Друг")

print(fixed) // "Привет, гость! Друг, добро пожаловать."

Заменилось только слово с заглавной буквы «Гость», потому что второе слово в тексте пишется со строчной «гость» — метод ищет точное совпадение регистра.

Разбор и сборка: split и joined

split(separator:) делает обратное объединению — разрезает строку по разделителю на части. Результат — набор кусочков текста (сам тип этого набора и его методы подробно разберём позже, когда дойдём до коллекций; пока достаточно знать, что это упорядоченный список частей строки).

let csvLine = "Аня,19,Ташкент"
let parts = csvLine.split(separator: ",")

print(parts) // ["Аня", "19", "Ташкент"]

Обратная операция — joined(separator:): она склеивает части обратно в одну строку через заданный разделитель.

let rebuilt = parts.joined(separator: " | ")
print(rebuilt) // "Аня | 19 | Ташкент"

По умолчанию split(separator:) пропускает пустые части, если разделители встретились подряд: "a,,b".split(separator: ",") даст всего два элемента, "a" и "b", а не три с пустой строкой между ними.

Регистр: uppercased, lowercased, capitalized

uppercased() и lowercased() переводят все буквы строки в верхний или нижний регистр, а capitalized делает заглавной первую букву каждого слова.

let city = "ташкент"

print(city.uppercased())   // "ТАШКЕНТ"
print(city.capitalized)    // "Ташкент"
print("SWIFT LANG".lowercased()) // "swift lang"

Так как эти операции — обычные методы и свойство, они не меняют исходную переменную, а возвращают новую строку. Если результат нужен и дальше, его надо сохранить в переменную или константу.

Обрезка пробелов: trimmingCharacters

Пользователи часто оставляют случайные пробелы в начале и в конце введённого текста. Убрать их помогает trimmingCharacters(in: .whitespaces) — метод удаляет пробельные символы только по краям строки, внутри текста ничего не трогая.

let typed = "   Ташкент   "
let clean = typed.trimmingCharacters(in: .whitespaces)

print("[\(clean)]") // "[Ташкент]"

Многострочные строки

Когда текст занимает несколько строк — например, письмо или блок инструкции — используют литерал в тройных кавычках. Он начинается и заканчивается тремя двойными кавычками на отдельных строках.

let letter = """
    Здравствуйте!

    Спасибо, что прошли этот урок.
    До встречи на следующем.
    """

print(letter)

Здесь важно правило отступа: Swift ориентируется на отступ закрывающих трёх кавычек и убирает точно такой же отступ у каждой строки текста. Поэтому закрывающие кавычки принято ставить на том же уровне отступа, что и открывающие — тогда лишние пробелы слева не попадут в саму строку. Внутри такого литерала можно свободно использовать одинарные кавычки и переносы строк без экранирования.

Сравнение строк: == сравнивает содержимое

В некоторых языках оператор равенства для текста сравнивает, лежат ли две переменные по одному и тому же адресу в памяти. В Swift String — тип значения, и == у строк сравнивает именно содержимое, символ за символом, а не то, как строка была получена.

let a = "Swift"
let b = "Sw" + "ift"

print(a == b) // true — важно только содержимое

Даже если одна строка была введена напрямую, а другая собрана из кусочков через + или joined, результат сравнения зависит только от итогового текста.

Кратко: строка и число

String(число) превращает число в строку — это работает всегда и без сюрпризов.

let score = 97
let scoreText = String(score)
print("Результат: " + scoreText) // "Результат: 97"

Обратное преобразование — «превратить текст в число» — устроено сложнее: не любой текст является числом (в "abc" его нет), поэтому Swift возвращает здесь особое значение, которое умеет сообщать о неудаче. Разбираться с ним подробно будем в следующем уроке.

Частые ошибки

  • Забыть, что hasPrefix, hasSuffix и contains чувствительны к регистру, и удивляться, почему очевидное совпадение не находится.
  • Ждать, что replacingOccurrences, uppercased или trimmingCharacters изменят исходную переменную — они возвращают новую строку, результат нужно сохранить самому.
  • Считать, что split(separator:) сразу даёт готовую строку — на самом деле это набор частей, и для новой строки его нужно собрать через joined(separator:).
  • Ставить закрывающие тройные кавычки многострочного литерала с другим отступом, чем у текста, — тогда в строку попадают лишние пробелы или, наоборот, обрезаются нужные символы.
  • Пытаться сравнивать строки через === по аналогии со ссылочными типами — для String достаточно и правильно использовать обычный ==.

Резюме

  • contains, hasPrefix, hasSuffix — проверки на вхождение, начало и конец строки, возвращают Bool.
  • replacingOccurrences(of:with:) заменяет все вхождения фрагмента и возвращает новую строку.
  • split(separator:) разбирает строку на части, joined(separator:) склеивает части обратно через разделитель.
  • uppercased(), lowercased(), capitalized меняют регистр букв.
  • trimmingCharacters(in: .whitespaces) убирает пробелы по краям строки.
  • Литерал в тройных кавычках хранит многострочный текст; отступ определяется по закрывающим кавычкам.
  • == у строк сравнивает содержимое, а не то, как строка была построена.
  • String(число) надёжно превращает число в текст; обратное преобразование может не получиться и требует особого подхода, который разберём дальше.

Проверьте себя

3 вопроса

Нормализация введённого текста

Пользователи часто вводят текст с лишними пробелами по краям и в произвольном регистре. Напишите функцию normalizeText(_:), которая принимает строку и возвращает новую строку: без пробелов по краям и полностью в нижнем регистре.

Например, для " Hello World " функция должна вернуть "hello world".

Пересборка списка через разделитель

Есть строка, где слова разделены пробелами (одним или несколькими подряд). Напишите функцию reformatList(_:separator:), которая разбивает текст на слова и снова собирает их в одну строку, но уже через переданный разделитель separator.

Например, для текста "apple banana cherry" и разделителя "-" функция должна вернуть "apple-banana-cherry".