Работа с текст
Проблем
Кодът е верен, а не минава
Магазинът дава 10% отстъпка с код SUMMER10. Клиентът го преписва от рекламата, но пише с малки букви и случайно слага интервал отпред:
string code = Console.ReadLine();if (code == "SUMMER10"){ Console.WriteLine("Отстъпка 10%");}else{ Console.WriteLine("Невалиден код");} summer10Невалиден кодЗа човек кодът е същият. За програмата summer10 и SUMMER10 са различни текстове: първият има един символ повече, а буквите са други. Трябва да можеш да гледаш вътре в текста и да го оправяш.
Идеята
Текстът е поредица от символи
Текстът е подредени един след друг символи: букви, цифри, интервали, знаци. Всеки символ има индекс, като в масив. Първият е с индекс 0.
string code = "SUMMER10";Console.WriteLine(code.Length);Console.WriteLine(code[0]);Console.WriteLine(code[code.Length - 1]);8S0code.Lengthе броят на символите.code[0]е символът с индекс 0. Последният символ е с индексLength - 1.
Един символ има свой тип: char. Пишеш го в единични кавички: 'S', '0', ' '. Текстът е в двойни: "S" е текст с един символ, а не char.
И при текста, и при масива дължината е Length, без скоби.
Виж го
Символ по символ
Колко цифри има в телефонния номер? Минаваш с цикъл през индексите от 0 до последния и гледаш всеки символ:
string phone = "0888 123 456";int digits = 0;for (int i = 0; i < phone.Length; i++){ char c = phone[i]; if (c >= '0' && c <= '9') { digits++; }}Console.WriteLine("Цифри: " + digits);Цифри: 10c >= '0' && c <= '9' работи, защото всеки символ всъщност е число: кодът му в таблицата със символите. Цифрите от '0' до '9' имат поредни кодове, буквите от 'a' до 'z' също. Затова символите се сравняват като числа.
По-точно, за любопитните
- За такива проверки има готови методи:
char.IsDigit(c),char.IsLetter(c),char.IsUpper(c),char.IsLower(c). Те знаят и кирилицата. - Кодът на символ получаваш с превръщане:
(int) 'A'е 65. Обратно,(char) 66е'B'. Затова'a' + 1е числото 98, а не букватаb. - Без индекс:
foreach (char c in phone)минава направо през символите на текста.
Пробвай
Кой символ?
Какво ще отпечата програмата?
string word = "Поръчка";Console.WriteLine(word[3]);Console.WriteLine(word.Length);Индексите са П 0, о 1, р 2, ъ 3. Символът с индекс 3 е четвъртият: „ъ“. Дължината е броят на всички символи, 7. Последният индекс е 6.
Идеята
Методите на текста
Текстът идва с готови методи: действия, които викаш с точка след текста. Ето поправката на промо кода:
string code = " summer10";string clean = code.Trim().ToUpper();Console.WriteLine("[" + clean + "]");Console.WriteLine(clean == "SUMMER10");[SUMMER10]TrueTrim() маха интервалите отпред и отзад. ToUpper() прави всички букви главни. Методите се нареждат един след друг: първият връща текст и върху него се вика вторият.
Още няколко, върху имейл адрес:
string email = "ivan.petrov@mail.bg";Console.WriteLine(email.Contains("@"));Console.WriteLine(email.IndexOf("@"));Console.WriteLine(email.Substring(0, email.IndexOf("@")));Console.WriteLine(email.Replace(".", " "));Console.WriteLine(email.EndsWith(".bg"));True11ivan.petrovivan petrov@mail bgTrue| Метод | Какво прави |
|---|---|
Contains(x) |
има ли го x вътре |
IndexOf(x) |
на кой индекс е x за първи път (-1, ако го няма) |
Substring(a, n) |
част от текста |
Replace(x, y) |
всяко x се заменя с y |
StartsWith(x), EndsWith(x) |
започва ли (свършва ли) с x |
ToLower() |
всички букви малки |
По-точно, за любопитните
Substring(a, n) взима n символа, като започва от индекс a. Вторият аргумент е броят символи, не индекс (в Java е обратното, там е индексът на края). Само с един аргумент взима всичко до края:
string date = "2026-10-24";Console.WriteLine(date.Substring(5, 2));Console.WriteLine(date.Substring(8));1024Идеята
Текстът не се променя
Има една особеност, която обърква всички в началото:
string name = "мария";name.ToUpper();Console.WriteLine(name);name = name.ToUpper();Console.WriteLine(name);марияМАРИЯВторият ред не прави нищо видимо. ToUpper() не променя name. Той прави нов текст с главни букви и го връща. Никой не го записва и той се губи.
Веднъж създаден, текстът не може да се промени. Казва се, че е неизменим (immutable). Всеки метод, който „променя“ текст, всъщност връща нов. За да го запазиш, присвояваш: name = name.ToUpper();.
string name = "мария";name.ToUpper();Console.WriteLine(name);name = name.ToUpper();Console.WriteLine(name);Стъпка по стъпка, с думи
- В name е текстът „мария“: 5 символа с индекси от 0 до 4.
- ToUpper() не пипа „мария“. Прави нов текст „МАРИЯ“.
- Никой не записва новия текст и той се губи.
- В name си е „мария“ и то се отпечатва.
- Пак нов текст „МАРИЯ“, но сега = го записва в name. Старото „мария“ отпада.
- Сега в name е „МАРИЯ“.
По-точно, за любопитните
- Затова
+=върху текст в дълъг цикъл е бавно: всяко завъртане прави нов текст и копира всичко досега. За стотици хиляди долепвания имаStringBuilder, обект, който може да се променя. Ще го срещнеш, когато стигнем до обектите.
Пробвай
Намери грешката
Потребителят въвежда имейла си с главни букви и интервали. Програмата трябва да го изчисти и да го пусне, но казва „Няма такъв потребител.“. На кой ред е грешката?
Избери реда с грешката.
email.Trim(); връща нов текст без интервалите, но той не се записва никъде. email си остава с интервалите. Поправката е email = email.Trim();, както на ред 2.
Идеята
Текстът на части
Често текстът съдържа няколко неща, разделени със знак: продукти със запетая, думи с интервал. Split го разделя на части и връща масив от текстове:
string list = "мляко,хляб,яйца,сирене";string[] items = list.Split(',');Console.WriteLine("Продукти: " + items.Length);for (int i = 0; i < items.Length; i++){ Console.WriteLine((i + 1) + ". " + items[i]);}Продукти: 41. мляко2. хляб3. яйца4. сиренеРазделителят не влиза в частите. Оттук нататък работиш с обикновен масив: дължина, индекси, цикъл.
Така се чете и цял ред числа от клавиатурата: разделяш го и превръщаш всяка част в число. Точно това ти липсваше в миналия урок за таблиците:
string[] parts = Console.ReadLine().Split(' ');int sum = 0;foreach (string p in parts){ sum += int.Parse(p);}Console.WriteLine(sum);42 38 51 47 60238По-точно, за любопитните
- Два интервала един до друг дават празна част
""между тях.Split(' ', StringSplitOptions.RemoveEmptyEntries)пропуска празните части. - Разделителите може да са няколко:
Split(',', ';'). - Обратното на
Splitеstring.Join(", ", items): долепва частите с разделител.
Пробвай
Едно и също ли е?
В C# == сравнява съдържанието на текстовете, буква по буква. Но главната и малката буква са различни символи. Въведено е София:
Какво ще отпечата програмата?
string city = Console.ReadLine();Console.WriteLine(city == "София");Console.WriteLine(city == "софия");Console.WriteLine(city.ToLower() == "софия");city == "София" е True: буквите са същите. "софия" започва с малко „с“, а то е друг символ, затова е False. Ако главните и малките букви нямат значение, сравни двата текста с малки букви: city.ToLower() == "софия".
Има и метод за същото: string.Equals(city, "софия", StringComparison.OrdinalIgnoreCase). В Java == за текст не сравнява буквите, а там се ползва equals. Ако някога четеш Java код, имай го предвид.
Упражнение
Имена и думи
Реши задачите в редактора си. Текстът се чете от клавиатурата като цял ред.
1. Името наобратно. Прочети име и го отпечатай отзад напред. Минаваш през индексите от последния към 0 и долепваш символите в нов текст.
МарияяираМ2. Брой думи. Прочети изречение и преброй думите. Между думите може да има повече от един интервал, а в началото и в края може да има интервали. Подсказка: дума започва там, където символът не е интервал, а предният е интервал (или няма преден).
Днес е хубав денДуми: 43. Палиндром. Палиндромът се чете еднакво отпред назад и отзад напред. Интервалите и главните букви не се броят. Махни интервалите, направи буквите малки и сравни първия символ с последния, втория с предпоследния и т.н.
Аз обичам мач и бозаПалиндром еКафе с млякоНе е палиндромУпражнение
Проверки и броене
4. Сигурна парола. Питай за парола, докато не въведат сигурна: поне 8 знака, поне една цифра, една главна и една малка буква. След всяка слаба парола кажи какво се иска. Подсказка: do-while и проверка на всеки символ с char.IsDigit, char.IsUpper, char.IsLower.
kafekafe2026Kafe2026Парола:Слаба парола: поне 8 знака, цифра, главна и малка буква.Парола:Слаба парола: поне 8 знака, цифра, главна и малка буква.Парола:Паролата е приета.Още задачи
- Запетаи вместо интервали. Прочети изречение и замени всеки интервал със запетая.
Утре заминаваме за Варна→Утре,заминаваме,за,Варна. - Най-дългото име. Прочети имена, разделени със запетая, и отпечатай най-дългото.
Ана,Кристина,Петър→Кристина. - Гласните със звездичка. Замени всяка гласна (а, ъ, о, у, е, и) със
*, независимо дали е главна или малка.Обичам кафе→*б*ч*м к*ф*. - Честота на буквите. Преброй колко пъти се среща всяка буква в текста, без значение главна или малка. Подсказка: масив с 32 брояча, по един за всеки символ от
'а'до'я'(в таблицата със символите между тях има и две руски букви, ы и э, техните броячи просто остават 0). Индексът на букватаcеc - 'а'. Отпечатай само буквите, които се срещат. - Графика на символите. Като горната задача, но вместо числото отпечатай толкова звездички:
а: ****. - Колко пъти всяка дума. Прочети изречение и за всяка различна дума отпечатай колко пъти се среща. Подсказка: раздели на думи и за всяка дума провери дали вече не е броена по-рано в масива.
- Сортиране по дължина. Прочети думи, разделени с интервал, и ги отпечатай от най-късата към най-дългата. Сортирай масива както сортираше числа, но сравнявай дължините.
- Брой изречения. Преброй изреченията в текст: всяко свършва с
.,!или?. Внимавай с...и?!: те завършват едно изречение, не три. - Балансирани скоби. Провери дали скобите в текста са балансирани. С един вид скоби стига брояч:
(добавя 1,)вади 1. Броячът не бива да пада под 0 и накрая трябва да е 0. С три вида,(),[],{}, ти трябва масив от символи, в който пазиш отворените скоби, и брояч докъде е пълен: при затваряща скоба последната отворена трябва да е от същия вид.{[()]}е балансиран,([)]не е.
Тест