Работа с текст
Проблем
Кодът е верен, а не минава
Магазинът дава 10% отстъпка с код SUMMER10. Клиентът го преписва от рекламата, но пише с малки букви и случайно слага интервал отпред:
Scanner in = new Scanner(System.in);String code = in.nextLine();if (code.equals("SUMMER10")) { System.out.println("Отстъпка 10%");} else { System.out.println("Невалиден код");} summer10Невалиден кодЗа човек кодът е същият. За програмата summer10 и SUMMER10 са различни текстове: първият има един символ повече, а буквите са други. Трябва да можеш да гледаш вътре в текста и да го оправяш.
Идеята
Текстът е поредица от символи
Текстът е подредени един след друг символи: букви, цифри, интервали, знаци. Всеки символ има индекс, като в масив. Първият е с индекс 0.
String code = "SUMMER10";System.out.println(code.length());System.out.println(code.charAt(0));System.out.println(code.charAt(code.length() - 1));8S0code.length()е броят на символите.code.charAt(0)е символът с индекс 0. Последният символ е с индексlength() - 1.
Един символ има свой тип: char. Пишеш го в единични кавички: 'S', '0', ' '. Текстът е в двойни: "S" е текст с един символ, а не char.
Внимавай със скобите: при текста е length(), с кръгли скоби, защото е метод. При масива е length, без скоби.
Виж го
Символ по символ
Колко цифри има в телефонния номер? Минаваш с цикъл през индексите от 0 до последния и гледаш всеки символ:
String phone = "0888 123 456";int digits = 0;for (int i = 0; i < phone.length(); i++) { char c = phone.charAt(i); if (c >= '0' && c <= '9') { digits++; }}System.out.println("Цифри: " + digits);Цифри: 10c >= '0' && c <= '9' работи, защото всеки символ всъщност е число: кодът му в таблицата със символите. Цифрите от '0' до '9' имат поредни кодове, буквите от 'a' до 'z' също. Затова символите се сравняват като числа.
По-точно, за любопитните
- За такива проверки има готови методи:
Character.isDigit(c),Character.isLetter(c),Character.isUpperCase(c),Character.isLowerCase(c). Те знаят и кирилицата. - Кодът на символ получаваш с превръщане:
(int) 'A'е 65. Обратно,(char) 66е'B'. Затова'a' + 1е числото 98, а не букватаb. - Без индекс:
for (char c : phone.toCharArray())минава през символите, както for-each минава през масив.
Пробвай
Кой символ?
Какво ще отпечата програмата?
String word = "Поръчка";System.out.println(word.charAt(3));System.out.println(word.length());Индексите са П 0, о 1, р 2, ъ 3. Символът с индекс 3 е четвъртият: „ъ“. Дължината е броят на всички символи, 7. Последният индекс е 6.
Идеята
Методите на текста
Текстът идва с готови методи: действия, които викаш с точка след текста. Ето поправката на промо кода:
String code = " summer10";String clean = code.trim().toUpperCase();System.out.println("[" + clean + "]");System.out.println(clean.equals("SUMMER10"));[SUMMER10]truetrim() маха интервалите отпред и отзад. toUpperCase() прави всички букви главни. Методите се нареждат един след друг: първият връща текст и върху него се вика вторият.
Още няколко, върху имейл адрес:
String email = "ivan.petrov@mail.bg";System.out.println(email.contains("@"));System.out.println(email.indexOf("@"));System.out.println(email.substring(0, email.indexOf("@")));System.out.println(email.replace(".", " "));System.out.println(email.endsWith(".bg"));true11ivan.petrovivan petrov@mail bgtrue| Метод | Какво прави |
|---|---|
contains(x) |
има ли го x вътре |
indexOf(x) |
на кой индекс е x за първи път (-1, ако го няма) |
substring(a, b) |
част от текста |
replace(x, y) |
всяко x се заменя с y |
startsWith(x), endsWith(x) |
започва ли (свършва ли) с x |
toLowerCase() |
всички букви малки |
По-точно, за любопитните
substring(a, b) взима символите от индекс a до индекс b, без b. Броят им е b - a. Само с един аргумент взима всичко до края:
String date = "2026-10-24";System.out.println(date.substring(5, 7));System.out.println(date.substring(8));1024Идеята
Текстът не се променя
Има една особеност, която обърква всички в началото:
String name = "мария";name.toUpperCase();System.out.println(name);name = name.toUpperCase();System.out.println(name);марияМАРИЯВторият ред не прави нищо видимо. toUpperCase() не променя name. Той прави нов текст с главни букви и го връща. Никой не го записва и той се губи.
Веднъж създаден, текстът не може да се промени. Казва се, че е неизменим (immutable). Всеки метод, който „променя“ текст, всъщност връща нов. За да го запазиш, присвояваш: name = name.toUpperCase();.
String name = "мария";name.toUpperCase();System.out.println(name);name = name.toUpperCase();System.out.println(name);Стъпка по стъпка, с думи
- В name е текстът „мария“: 5 символа с индекси от 0 до 4.
- toUpperCase() не пипа „мария“. Прави нов текст „МАРИЯ“.
- Никой не записва новия текст и той се губи.
- В name си е „мария“ и то се отпечатва.
- Пак нов текст „МАРИЯ“, но сега = го записва в name. Старото „мария“ отпада.
- Сега в name е „МАРИЯ“.
По-точно, за любопитните
- Затова
+=върху текст в дълъг цикъл е бавно: всяко завъртане прави нов текст и копира всичко досега. За стотици хиляди долепвания имаStringBuilder, обект, който може да се променя. Ще го видим в модул „Текст и файлове“.
Пробвай
Намери грешката
Потребителят въвежда имейла си с главни букви и интервали. Програмата трябва да го изчисти и да го пусне, но казва „Няма такъв потребител.“. На кой ред е грешката?
Избери реда с грешката.
email.trim(); връща нов текст без интервалите, но той не се записва никъде. email си остава с интервалите. Поправката е email = email.trim();, както на ред 2.
Идеята
Текстът на части
Често текстът съдържа няколко неща, разделени със знак: продукти със запетая, думи с интервал. split го разделя на части и връща масив от текстове:
String list = "мляко,хляб,яйца,сирене";String[] items = list.split(",");System.out.println("Продукти: " + items.length);for (int i = 0; i < items.length; i++) { System.out.println((i + 1) + ". " + items[i]);}Продукти: 41. мляко2. хляб3. яйца4. сиренеРазделителят не влиза в частите. Оттук нататък работиш с обикновен масив: дължина, индекси, цикъл.
Така се чете и цял ред числа от клавиатурата: разделяш го и превръщаш всяка част в число. Точно това ти липсваше в миналия урок за таблиците:
Scanner in = new Scanner(System.in);String[] parts = in.nextLine().split(" ");int sum = 0;for (String p : parts) { sum += Integer.parseInt(p);}System.out.println(sum);42 38 51 47 60238По-точно, за любопитните
splitполучава регулярен израз, не обикновен текст. Повечето знаци работят направо, но.значи „всеки символ“: за точка пишиsplit("\\."). Регулярните изрази идват в модул „Текст и файлове“.- Два интервала един до друг дават празна част
""между тях.split(" +")(един или повече интервала) решава това. - Обратното на
splitеString.join(", ", items): долепва частите с разделител.
Пробвай
== или equals?
В първата карта промо кодът се сравняваше с equals. Защо не с ==, както числата? Въведено е admin:
Какво ще отпечата програмата?
Scanner in = new Scanner(System.in);String user = in.nextLine();System.out.println(user == "admin");System.out.println(user.equals("admin"));При текстовете == не сравнява буквите. Той проверява дали двете страни са един и същ обект в паметта. Текстът, прочетен от клавиатурата, е нов обект, а "admin" в кода е друг. Буквите са еднакви, обектите не. equals сравнява буквите и казва true.
Правилото: текст в Java се сравнява винаги с equals. Ако главните и малките букви нямат значение, ползвай equalsIgnoreCase.
По-точно, за любопитните
Понякога == „работи“: "admin" == "admin" е true, защото Java пази еднаквите текстове от кода в един обект. Затова грешката минава в проба с текст от кода и излиза едва с истински вход. Не разчитай на това.
Упражнение
Имена и думи
Реши задачите в редактора си. Текстът се чете от клавиатурата като цял ред.
1. Името наобратно. Прочети име и го отпечатай отзад напред. Минаваш през индексите от последния към 0 и долепваш символите в нов текст.
МарияяираМ2. Брой думи. Прочети изречение и преброй думите. Между думите може да има повече от един интервал, а в началото и в края може да има интервали. Подсказка: дума започва там, където символът не е интервал, а предният е интервал (или няма преден).
Днес е хубав денДуми: 43. Палиндром. Палиндромът се чете еднакво отпред назад и отзад напред. Интервалите и главните букви не се броят. Махни интервалите, направи буквите малки и сравни първия символ с последния, втория с предпоследния и т.н.
Аз обичам мач и бозаПалиндром еКафе с млякоНе е палиндромУпражнение
Проверки и броене
4. Сигурна парола. Питай за парола, докато не въведат сигурна: поне 8 знака, поне една цифра, една главна и една малка буква. След всяка слаба парола кажи какво се иска. Подсказка: do-while и проверка на всеки символ с Character.isDigit, Character.isUpperCase, Character.isLowerCase.
kafekafe2026Kafe2026Парола:Слаба парола: поне 8 знака, цифра, главна и малка буква.Парола:Слаба парола: поне 8 знака, цифра, главна и малка буква.Парола:Паролата е приета.Още задачи
- Запетаи вместо интервали. Прочети изречение и замени всеки интервал със запетая.
Утре заминаваме за Варна→Утре,заминаваме,за,Варна. - Най-дългото име. Прочети имена, разделени със запетая, и отпечатай най-дългото.
Ана,Кристина,Петър→Кристина. - Гласните със звездичка. Замени всяка гласна (а, ъ, о, у, е, и) със
*, независимо дали е главна или малка.Обичам кафе→*б*ч*м к*ф*. - Честота на буквите. Преброй колко пъти се среща всяка буква в текста, без значение главна или малка. Подсказка: масив с 32 брояча, по един за всеки символ от
'а'до'я'(в таблицата със символите между тях има и две руски букви, ы и э, техните броячи просто остават 0). Индексът на букватаcеc - 'а'. Отпечатай само буквите, които се срещат. - Графика на символите. Като горната задача, но вместо числото отпечатай толкова звездички:
а: ****. - Колко пъти всяка дума. Прочети изречение и за всяка различна дума отпечатай колко пъти се среща. Подсказка: раздели на думи и за всяка дума провери дали вече не е броена по-рано в масива.
- Сортиране по дължина. Прочети думи, разделени с интервал, и ги отпечатай от най-късата към най-дългата. Сортирай масива както сортираше числа, но сравнявай дължините.
- Брой изречения. Преброй изреченията в текст: всяко свършва с
.,!или?. Внимавай с...и?!: те завършват едно изречение, не три. - Балансирани скоби. Провери дали скобите в текста са балансирани. С един вид скоби стига брояч:
(добавя 1,)вади 1. Броячът не бива да пада под 0 и накрая трябва да е 0. С три вида,(),[],{}, ти трябва масив от символи, в който пазиш отворените скоби, и брояч докъде е пълен: при затваряща скоба последната отворена трябва да е от същия вид.{[()]}е балансиран,([)]не е.
Тест