JavaRush /Курси /Swift SELF /Методи String: isEmpty, count, contains, hasPrefix

Методи String: isEmpty, count, contains, hasPrefix

Swift SELF
Рівень 7 , Лекція 3
Відкрита

1. Розширена робота з рядками

Рядки — це місце, де реальний світ стикається з вашим кодом і одразу намагається його зламати. Користувач може ввести порожній рядок, три пробіли, дивне імʼя файлу або команду, якої ви не очікували. Якщо ви не перевіряєте введення заздалегідь, програма або поводиться «якось дивно», або падає, або виводить беззмістовні повідомлення.

Добра новина: для найчастіших перевірок нам не потрібні ні складні алгоритми, ні індекси рядків. Достатньо чотирьох інструментів: isEmpty, count, contains, hasPrefix/hasSuffix.

Уявімо, що ми потроху пишемо невеличкий консольний застосунок TaskBox — умовний «помічник із задачами». Поки що він уміє читати один рядок введення й реагувати на нього. Сьогодні ми підсилимо його так, щоб він не панікував через порожнє введення й умів розпізнавати прості «формати» рядків.

Популярні властивості й методи String

У String у Swift є доволі багато корисних властивостей і методів. Ми не будемо вивчати їх усі одразу, але корисно знати кілька найпопулярніших — саме вони найчастіше трапляються у повсякденному коді.

Властивість / метод Що робить Приклад
text.isEmpty
Перевіряє, чи рядок порожній
"".isEmpty → true
text.count
Кількість символів у рядку
"Swift".count → 5
text.contains("abc")
Перевіряє, чи містить підрядок
"hello".contains("ell")
text.hasPrefix("abc")
Перевіряє початок рядка
"file.txt".hasPrefix("file")
text.hasSuffix("abc")
Перевіряє кінець рядка
"file.txt".hasSuffix(".txt")
text.lowercased()
Перетворює рядок на нижній регістр
"Swift".lowercased()
text.uppercased()
Перетворює рядок на верхній регістр
"Swift".uppercased()
text.trimmingCharacters
Прибирає пробіли на краях рядка
" hi ".trimmingCharacters(...)

Наприклад:

let text = "Swift"

print(text.isEmpty)        // false
print(text.count)          // 5
print(text.contains("wi")) // true
print(text.hasPrefix("Sw")) // true
print(text.hasSuffix("ft")) // true

У цій лекції ми докладно розглянемо чотири найпрактичніші інструменти:

  • isEmpty
  • count
  • contains
  • hasPrefix / hasSuffix

Їх достатньо, щоб зробити введення користувача набагато надійнішим і зрозумілішим.

2. isEmpty: найпростіший спосіб запитати «ти порожній?»

Коли ви вперше хочете перевірити, чи рядок порожній, майже завжди тягнеться рука написати щось на кшталт text.count == 0. Це не помилка, але Swift дає читабельніший варіант: text.isEmpty. Він буквально читається як «рядок порожній?» і майже завжди робить код зрозумілішим з першого погляду.

Важливо: isEmpty — це властивість, тому жодних дужок, тобто не isEmpty(). Так само, як і у count. А ще: якщо ви читаєте введення через readLine(), спочатку подбайте, щоб у вас був саме рядок, а не String?.

Почнімо з найпрактичнішого: безпечно читаємо введення й перевіряємо, чи рядок порожній.

let line = readLine() ?? ""

if line.isEmpty {
    print("Ви нічого не ввели.") // Ви нічого не ввели.
} else {
    print("Гаразд, ви ввели: \(line)") // наприклад: Гаразд, ви ввели: buy milk
}

Зверніть увагу на дві речі. По-перше, readLine() може повернути nil, і тоді ?? "" рятує нас, перетворюючи «відсутність введення» на порожній рядок. По-друге, isEmpty робить умову короткою й читабельною.

Іноді хочеться зробити повідомлення дружнішим — так, ми пишемо консоль, але це не привід сумувати:

let name = readLine() ?? ""

let message = name.isEmpty ? "Привіт, незнайомцю!" : "Привіт, \(name)!"
print(message) // залежить від введення

Тут тернарний оператор допомагає не плодити зайвий if/else, але він залишається досить читабельним, бо умова коротка.

3. count: довжина рядка як «поріг здорового глузду»

Дуже часто рядок потрібно перевіряти не на «порожньо / не порожньо», а на те, чи достатньо він довгий. Наприклад, імʼя користувача з однієї літери формально можливе, але зазвичай це або випадковість, або введення на кшталт «а». Для таких правил використовують count. Ця властивість є в багатьох колекціях Swift, і рядок — не виняток.

Важливо зрозуміти просту модель: count — це кількість символів у рядку. Ми поки що не заглиблюємося в деталі Unicode — це окрема велика тема, — але тримаємо в голові практичне правило: count — це те, що зазвичай очікує людина, коли запитує: «Скільки літер у слові?».

Приклад: вимагаємо, щоб назва задачі в TaskBox була хоча б із 3 символів.

let title = readLine() ?? ""

if title.count < 3 {
    print("Занадто коротко. Потрібно щонайменше 3 символи.") // ...
} else {
    print("Додаємо задачу: \(title)") // ...
}

А тепер акуратна демонстрація того, чому isEmpty часто кращий за count == 0. Технічно обидва підходи працюють, але isEmpty читається простіше.

let text = readLine() ?? ""

if text.count == 0 {
    print("Порожньо (через count).") // ...
}

if text.isEmpty {
    print("Порожньо (через isEmpty).") // ...
}

У реальному проєкті ви оберете один стиль, зазвичай isEmpty, і дотримуватиметеся його, щоб код виглядав однаково.

Ще один частий шаблон: виводити попередження, якщо рядок занадто довгий. Наприклад, обмежимо назву задачі 30 символами, щоб виведення в консолі не перетворювалося на нескінченний роман.

let title = readLine() ?? ""

if title.count > 30 {
    print("Занадто довга назва (\(title.count)).") // ...
} else {
    print("Гаразд: \(title)") // ...
}

4. contains: «чи є всередині потрібний фрагмент?»

Коли ви перевіряєте рядок на відповідність простому формату, вам часто не потрібна точна структура — достатньо зрозуміти, чи трапляється всередині важливий символ або шматочок тексту. Для цього є contains(...). Сенс дуже буквальний: «рядок містить підрядок?». Для новачка це один із найприємніших методів, тому що він позбавляє ручних циклів і пошуку по літерах.

Класичний приклад — не ідеальна валідація, але для початкового рівня корисна: перевірити, що в адресі електронної пошти взагалі є символ @.

let email = readLine() ?? ""

if email.contains("@") {
    print("Схоже на електронну пошту (принаймні є @).") // ...
} else {
    print("Не схоже на електронну пошту: немає @.") // ...
}

Тут важливо не перебільшувати: наявність @ ще не гарантує коректну адресу електронної пошти, але як мінімальна перевірка — уже щось.

contains допомагає й для команд. Припустімо, у TaskBox ми хочемо помітити, що користувач десь написав слово "help" (навіть якщо він вводить щось на кшталт "help me please").

let line = readLine() ?? ""

if line.contains("help") {
    print("Підказка: введіть add <текст> або exit") // ...
} else {
    print("Команду не розпізнано.") // ...
}

Тут зʼявляється важливий нюанс, який ми не розглядатимемо сьогодні, але чесно про нього скажемо: contains чутливий до регістру. Тобто "Help" і "help" — різні рядки. У наступній лекції ми акуратно нормалізуємо введення, щоб такі перевірки працювали стабільніше. Поки що просто памʼятаємо: порівняння відбувається «буква в букву».

5. hasPrefix і hasSuffix: перевіряємо початок і кінець рядка

Іноді contains занадто «розмитий»: рядок може містити "add" десь посередині, але нам потрібно, щоб команда починалася з "add ", інакше це не команда, а збіг.

Саме тут у пригоді стають hasPrefix і hasSuffix. Це спеціалізовані перевірки початку та кінця рядка.

Найжиттєвіший сценарій — команди в консолі. Нехай у нашому TaskBox команда додавання виглядає так: користувач пише "add Buy milk". Ми поки що не вміємо акуратно розбирати аргументи через split (це буде пізніше), але вже можемо розпізнати, що рядок починається з "add ".

let line = readLine() ?? ""

if line.hasPrefix("add ") {
    print("Схоже, це команда додавання.") // ...
} else {
    print("Це не команда add.") // ...
}

Другий класичний сценарій — перевірка розширення файлу. Наприклад, якщо користувач вводить імʼя файлу для експорту задач, і ми хочемо приймати лише ".txt".

let filename = readLine() ?? ""

if filename.hasSuffix(".txt") {
    print("Гаразд, це .txt-файл.") // ...
} else {
    print("Потрібна назва файлу, що закінчується на .txt") // ...
}

Тут корисно бути уважним: суфікс порівнюється буквально. Якщо ви забудете крапку й напишете "txt", то рядок "report.txt" теж «закінчується на txt», але ви втрачаєте точність і ризикуєте прийняти "mytxt" за «файл txt». Тому для розширень зазвичай пишуть саме ".txt".

Шпаргалка: що обирати в якій ситуації

Коли методів кілька, іноді важко одразу обрати: «мені contains чи hasPrefix?». Це нормально. Щоб зменшити когнітивне навантаження, корисно тримати перед очима просту таблицю «питання → інструмент». Це не магія і не догма, а шпаргалка, яка економить час.

Що хочемо дізнатися про рядок Як запитуємо це в Swift Типовий приклад
Рядок порожній?
text.isEmpty
користувач нічого не ввів
Довжина рядка
text.count
мінімальна довжина пароля / назви
Чи є фрагмент усередині
text.contains("...")
чи є @ в адресі електронної пошти
Починається з фрагмента
text.hasPrefix("...")
команда починається з "add "
Закінчується фрагментом
text.hasSuffix("...")
файл закінчується на ".txt"

Ці методи можна комбінувати, але краще робити це поступово й читабельно: спочатку проста перевірка (порожньо / не порожньо), а вже потім суворіші правила.

6. Приклад: підсилюємо TaskBox без складного парсингу

Зараз ми зберемо заготовку для майбутнього проєкту: програма читає один рядок і вирішує, що відповісти. Ми свідомо не робимо складний розбір аргументів, не використовуємо split і не ліземо в індекси рядка. Наша мета — показати, як чотири методи допомагають зробити поведінку передбачуваною вже сьогодні, навіть якщо застосунок ще дуже простий.

Ось варіант «однієї ітерації» TaskBox: команда "help", команда "exit", команда "add " і проста перевірка, що після "add " узагалі щось є.

let line = readLine() ?? ""

if line.isEmpty {
    print("Введіть команду. Наприклад: help") // ...
} else if line == "help" {
    print("Команди: help, add <текст>, exit") // ...
} else if line == "exit" {
    print("Вихід.") // Вихід.
} else if line.hasPrefix("add ") {
    let hasTextAfterAdd = line.count > 4
    print(hasTextAfterAdd ? "Додаємо задачу." : "Після add потрібен текст.") // ...
} else {
    print("Невідома команда: \(line)") // ...
}

Чому line.count > 4? Тому що "add " — це 4 символи: a, d, d і пробіл. Якщо довжина дорівнює 4, отже користувач увів лише «add » і не додав текст. Так, це трохи простувато, але чудово підходить для поточного рівня, поки ми не вивчили розбір рядка на частини.

А тепер додамо ще один захист: іноді користувач вводить "exit!!!" або "help please". Ми можемо підказати йому, що він, схоже, мав на увазі. Для цього підключаємо contains.

let line = readLine() ?? ""

if line.contains("help") {
    print("Схоже, ви просите help. Команди: help, add <текст>, exit") // ...
} else if line.contains("exit") {
    print("Схоже, ви хочете вийти. Введіть рівно: exit") // ...
} else {
    print("Гаразд, продовжуємо.") // ...
}

У цьому прикладі ми навмисно робимо підказку «мʼякою»: contains — це не сувора перевірка команди, а «схоже на…». Це хороший стиль: ви використовуєте розмитий метод там, де розмитість доречна, тобто для підказок, і суворий метод там, де потрібна строгість, наприклад hasPrefix("add ").

Схема: типовий конвеєр перевірок рядка

Коли перевірок стає кілька, корисно мислити не як «я накидав 10 if-ів», а як про маленький конвеєр: спочатку перевіряємо найпростіше й очевидне, а потім уточнюємо формат. Така модель допомагає писати код без хаосу і не забувати базові випадки.

flowchart TD
    A["Отримали line (String)"] --> B{line.isEmpty?}
    B -- так --> C["Повідомлення: 'Введіть команду'"]
    B -- ні --> D{line == 'help' або 'exit'?}
    D -- так --> E["Виконуємо просту команду"]
    D -- ні --> F{"line.hasPrefix('add ' )?"}
    F -- так --> G{line.count > 4?}
    G -- так --> H["Приймаємо текст задачі"]
    G -- ні --> I["Помилка: 'Після add потрібен текст'"]
    F -- ні --> J["Невідома команда"]

Зверніть увагу: тут немає складної магії. Просто порядок: порожньо → точні команди → команда з префіксом → перевірка довжини → інакше невідомо. Саме так народжується передбачувана поведінка.

7. Типові помилки

Помилка №1: плутати властивість і метод (count() і isEmpty()).
У Swift count і isEmpty — це властивості, тож дужок у них немає. Якщо написати text.count(), компілятор справедливо скаже, що ви викликаєте те, чого не існує. Коли вагаєтеся, згадуйте: «властивість — це характеристика», тому без дужок.

Помилка №2: робити line[0] або «перевірю перший символ напряму».
Після кількох задач багатьом хочеться одразу перевірити перший символ, і рука тягнеться до індексації, як в інших мовах. У Swift із рядками так не можна (і ми поки що не вивчали рядкові індекси). Сьогодні у нас є простіший і безпечніший інструмент: hasPrefix, який прямо й читабельно виражає намір.

Помилка №3: використовувати contains там, де потрібна сувора команда.
Якщо ви перевіряєте команду виходу через line.contains("exit"), то рядок "noexitplease" теж «містить exit». У підсумку застосунок почне виходити тоді, коли користувач узагалі цього не просив. Для команд використовуйте точне порівняння (==) або hasPrefix, а contains залишайте для підказок і «приблизно схожого».

Помилка №4: забувати про регістр і дивуватися, що contains("help") не спрацював на "Help".
За замовчуванням порівняння рядків чутливі до регістру: "Help" != "help". Це не баг, а правило. Якщо вам потрібно, щоб команда розпізнавалася незалежно від регістру, це вирішується нормалізацією введення (ми будемо робити це далі), але сьогодні важливо хоча б памʼятати, чому «ніби однаково» не спрацювало.

Помилка №5: звертатися до перевірок до того, як ви перетворили readLine() на звичайний String.
readLine() повертає String?, і якщо ви спробуєте викликати isEmpty на опціоналі, компілятор не дозволить. Правильний перший крок на нашому поточному рівні — let line = readLine() ?? "", і лише потім будь-які перевірки рядка.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ