Перейти к содержимому
Теория · Строки и символы · урок 6 из 7

Методы строк

Нитку с бусинами можно не только пересчитать. Её можно нанизать заново заглавными буквами, поискать на ней нужные бусины, разрезать на куски у бусин-разделителей и снова собрать куски вместе.

Вот задача, где это нужно. Пользователь прислал файл Photo.PNG, а программа принимает только картинки PNG и проверяет, что имя кончается на .png. Имя пришло с заглавными буквами, и прямая проверка его не узнаёт. Программа ниже показывает и проблему, и решение:

SWIFT
let file = "Photo.PNG"
let name = file.lowercased()
print(name, file.uppercased()) // photo.png PHOTO.PNG
print(name.hasPrefix("photo"), name.hasSuffix(".png")) // true true
print(file.hasSuffix(".png"), file.contains("Photo")) // false true
print(file) // Photo.PNG

Действия, которые строка умеет выполнять сама, называют методами. Метод — это функция, которая принадлежит строке. Его вызывают через точку, а в скобках передают то, что ему нужно: name.hasSuffix(".png").

Регистр букв

Буква бывает заглавной или строчной — это её регистр. Две строки, которые различаются только регистром, для Swift разные: "PNG" == "png" даёт false.

  • lowercased() возвращает копию строки, где все буквы строчные: photo.png.
  • uppercased() возвращает копию, где все буквы заглавные: PHOTO.PNG.

Оба метода возвращают новую строку, а исходная не меняется. Поэтому последняя строка программы печатает Photo.PNG, как было.

Чтобы сравнить строки без учёта регистра, обе приводят к одному регистру. Ответ пользователя проверяют так: answer.lowercased() == "yes", и тогда подходят и Yes, и YES.

Поиск в строке

Три метода отвечают на вопрос «есть ли в строке этот текст» и возвращают Bool:

  • hasPrefix("photo") — начинается ли строка с этого текста;
  • hasSuffix(".png") — заканчивается ли она им;
  • contains("Photo") — есть ли этот текст где-нибудь внутри.

Все три различают регистр. file.hasSuffix(".png") даёт false, ведь file кончается на .PNG. Поэтому проверяют name, где все буквы уже строчные.

Разбить и склеить

Строку со списком через запятую часто нужно разрезать на части, а потом собрать снова, но уже с другим разделителем:

SWIFT
let list = "tea,milk,,bread"
let items = list.split(separator: ",")
print(items.count) // 3
print(items.joined(separator: " + ")) // tea + milk + bread

split(separator:) — разрезать

split режет строку у каждого символа-разделителя, здесь у запятой. Он возвращает массив частей — ряд кусков строки по порядку. items.count даёт число частей.

Между двумя запятыми подряд кусок пустой, и split его отбрасывает. Поэтому частей три, а не четыре. Так же отбрасываются пустые куски в начале и в конце строки: ",a," даёт одну часть.

joined(separator:) — склеить

joined собирает части в одну строку и вставляет разделитель между ними. После последней части разделителя нет, поэтому программа печатает tea + milk + bread.

Здесь картинка с ниткой неточна. Настоящую нитку после разрезания уже не вернуть. А split не трогает list: после него строка по-прежнему целая, и её можно разрезать снова.

Строка задом наперёд

reversed() перебирает символы строки с конца. Сам по себе он возвращает не строку, поэтому новую строку из этих символов собирает String(…):

SWIFT
let word = "stressed"
print(String(word.reversed())) // desserts

Символы при этом переставляются целиком, а не по байтам. String("café".reversed()) даёт éfac, и буква é остаётся целой.

Перевернуть строку часто просят на собеседовании, например, чтобы проверить палиндром — слово, которое одинаково читается с обеих сторон. В Swift такая проверка занимает одну строку: word == String(word.reversed()).

Частая ошибка

Строку перевернули, но забыли String(…):

SWIFT
print(word.reversed()) // ✗

Программа компилируется без предупреждений, но печатает не desserts, а служебную запись вроде ReversedCollection<String>(_base: "stressed"). reversed() не собирает новую строку, и print показывает то, что получил, как есть. Оберните результат в String(…).

Главное

  • lowercased() и uppercased() возвращают новую строку в другом регистре, а исходная не меняется.
  • hasPrefix, hasSuffix и contains ищут текст в строке и различают регистр.
  • split(separator:) режет строку на части и отбрасывает пустые, а joined(separator:) склеивает части, ставя разделитель только между ними.
  • String(s.reversed()) переворачивает строку по символам; без String(…) получится не строка.