Методы строк
Нитку с бусинами можно не только пересчитать. Её можно нанизать заново заглавными буквами, поискать на ней нужные бусины, разрезать на куски у бусин-разделителей и снова собрать куски вместе.
Вот задача, где это нужно. Пользователь прислал файл Photo.PNG, а программа принимает только картинки PNG и проверяет, что имя кончается на .png. Имя пришло с заглавными буквами, и прямая проверка его не узнаёт. Программа ниже показывает и проблему, и решение:
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, где все буквы уже строчные.
Разбить и склеить
Строку со списком через запятую часто нужно разрезать на части, а потом собрать снова, но уже с другим разделителем:
let list = "tea,milk,,bread"
let items = list.split(separator: ",")
print(items.count) // 3
print(items.joined(separator: " + ")) // tea + milk + breadsplit(separator:) — разрезать
split режет строку у каждого символа-разделителя, здесь у запятой. Он возвращает массив частей — ряд кусков строки по порядку. items.count даёт число частей.
Между двумя запятыми подряд кусок пустой, и split его отбрасывает. Поэтому частей три, а не четыре. Так же отбрасываются пустые куски в начале и в конце строки: ",a," даёт одну часть.
joined(separator:) — склеить
joined собирает части в одну строку и вставляет разделитель между ними. После последней части разделителя нет, поэтому программа печатает tea + milk + bread.
Здесь картинка с ниткой неточна. Настоящую нитку после разрезания уже не вернуть. А split не трогает list: после него строка по-прежнему целая, и её можно разрезать снова.
Строка задом наперёд
reversed() перебирает символы строки с конца. Сам по себе он возвращает не строку, поэтому новую строку из этих символов собирает String(…):
let word = "stressed"
print(String(word.reversed())) // dessertsСимволы при этом переставляются целиком, а не по байтам. String("café".reversed()) даёт éfac, и буква é остаётся целой.
Перевернуть строку часто просят на собеседовании, например, чтобы проверить палиндром — слово, которое одинаково читается с обеих сторон. В Swift такая проверка занимает одну строку: word == String(word.reversed()).
Частая ошибка
Строку перевернули, но забыли String(…):
print(word.reversed()) // ✗Программа компилируется без предупреждений, но печатает не desserts, а служебную запись вроде ReversedCollection<String>(_base: "stressed"). reversed() не собирает новую строку, и print показывает то, что получил, как есть. Оберните результат в String(…).
Главное
lowercased()иuppercased()возвращают новую строку в другом регистре, а исходная не меняется.hasPrefix,hasSuffixиcontainsищут текст в строке и различают регистр.split(separator:)режет строку на части и отбрасывает пустые, аjoined(separator:)склеивает части, ставя разделитель только между ними.String(s.reversed())переворачивает строку по символам; безString(…)получится не строка.