Към съдържанието
wannacode

Работа с текст

Проблем

Кодът е верен, а не минава

Магазинът дава 10% отстъпка с код SUMMER10. Клиентът го преписва от рекламата, но пише с малки букви и случайно слага интервал отпред:

code = input()
if code == "SUMMER10":
print("Отстъпка 10%")
else:
print("Невалиден код")
Вход
summer10
Изход
Невалиден код

За човек кодът е същият. За програмата summer10 и SUMMER10 са различни текстове: първият има един символ повече, а буквите са други. Трябва да можеш да гледаш вътре в текста и да го оправяш.

Идеята

Текстът е поредица от символи

Текстът е подредени един след друг символи: букви, цифри, интервали, знаци. Всеки символ има индекс, като в масив. Първият е с индекс 0.

code = "SUMMER10"
print(len(code))
print(code[0])
print(code[len(code) - 1])
Изход
8
S
0
  • len(code) е броят на символите.
  • code[0] е символът с индекс 0. Последният символ е с индекс len(code) - 1.

Отделен тип за един символ (в Java и C# се казва char) тук няма. Символът е просто текст с дължина 1. Единичните и двойните кавички правят едно и също: 'S' и "S" са еднакви текстове.

code = "SUMMER10"
print(code[-1])
print(type(code[0]))
print(len(code[0]))
print('S' == "S")
Изход
0
<class 'str'>
1
True

code[-1] е последният символ: отрицателният индекс брои отзад, -1 е последният, -2 предпоследният. Това е по-кратко от code[len(code) - 1].

len дава дължината и на текста, и на списъка.

Виж го

Символ по символ

Колко цифри има в телефонния номер? Минаваш с цикъл през индексите от 0 до последния и гледаш всеки символ:

phone = "0888 123 456"
digits = 0
for i in range(len(phone)):
c = phone[i]
if c >= "0" and c <= "9":
digits += 1
print(f"Цифри: {digits}")
Изход
Цифри: 10

c >= "0" and c <= "9" работи, защото всеки символ има код: число в таблицата със символите. Текстовете се сравняват по кодовете на символите. Цифрите от "0" до "9" имат поредни кодове, буквите от "a" до "z" също.

По-точно, за любопитните
  • За такива проверки има готови методи: c.isdigit(), c.isalpha(), c.isupper(), c.islower(). Те знаят и кирилицата.
  • Кодът на символ дава ord("A"), тоест 65. Обратно, chr(66) е "B". Текст и число не се събират: "a" + 1 спира програмата с TypeError.
  • Без индекс: for c in phone: минава направо през символите на текста.
print("7".isdigit(), "ж".isalpha(), "Ж".isupper(), "ж".islower())
print(ord("A"), chr(66))
try:
print("a" + 1)
except TypeError as e:
print(f"TypeError: {e}")
digits = 0
for c in "0888 123 456":
if c.isdigit():
digits += 1
print(digits)
Изход
True True True True
65 B
TypeError: can only concatenate str (not "int") to str
10

Пробвай

Кой символ?

Какво ще отпечата програмата?

word = "Поръчка"
print(word[3])
print(len(word))
Отговор

Идеята

Методите на текста

Текстът идва с готови методи: действия, които викаш с точка след текста. Ето поправката на промо кода:

code = " summer10"
clean = code.strip().upper()
print(f"[{clean}]")
print(clean == "SUMMER10")
Изход
[SUMMER10]
True

strip() маха интервалите отпред и отзад. upper() прави всички букви главни. Методите се нареждат един след друг: първият връща текст и върху него се вика вторият.

Още няколко, върху имейл адрес:

email = "ivan.petrov@mail.bg"
print("@" in email)
print(email.find("@"))
print(email[0:email.find("@")])
print(email.replace(".", " "))
print(email.endswith(".bg"))
Изход
True
11
ivan.petrov
ivan petrov@mail bg
True
Метод Какво прави
x in email има ли го x вътре
find(x) на кой индекс е x за първи път (-1, ако го няма)
email[a:b] част от текста
replace(x, y) всяко x се заменя с y
startswith(x), endswith(x) започва ли (свършва ли) с x
lower() всички букви малки

"@" in email не е метод, а оператор: in пита дали текстът вляво се среща в текста вдясно. Частта от текста се взима с квадратни скоби и двоеточие, email[a:b], за нея има повече в „По-точно“ отдолу.

print("ivan.petrov@mail.bg".find("#"))
print("Кафе".startswith("К"))
print("Кафе".lower())
Изход
-1
True
кафе
По-точно, за любопитните

date[a:b] взима символите от индекс a до индекс b, без b. Броят им е b - a. Без край, date[a:], взима всичко до края. Отрицателният индекс брои отзад: date[-2:] са последните два символа.

date = "2026-10-24"
print(date[5:7])
print(date[8:])
print(date[-2:])
Изход
10
24
24

Идеята

Текстът не се променя

Има една особеност, която обърква всички в началото:

name = "мария"
name.upper()
print(name)
name = name.upper()
print(name)
Изход
мария
МАРИЯ

Вторият ред не прави нищо видимо. upper() не променя name. Той прави нов текст с главни букви и го връща. Никой не го записва и той се губи.

Веднъж създаден, текстът не може да се промени. Казва се, че е неизменим (immutable). Всеки метод, който „променя“ текст, всъщност връща нов. За да го запазиш, присвояваш: name = name.upper().

name = "мария"
name.upper()
print(name)
name = name.upper()
print(name)
Стъпка по стъпка, с думи
  1. В name е текстът „мария“: 5 символа с индекси от 0 до 4.
  2. upper() не пипа „мария“. Прави нов текст „МАРИЯ“.
  3. Никой не записва новия текст и той се губи.
  4. В name си е „мария“ и то се отпечатва.
  5. Пак нов текст „МАРИЯ“, но сега = го записва в name. Старото „мария“ отпада.
  6. Сега в name е „МАРИЯ“.
По-точно, за любопитните
  • Символ вътре в текста не може да се смени и с индекс: name[0] = "М" спира програмата с TypeError: 'str' object does not support item assignment. Правиш нов текст, например "М" + name[1:].
  • Когато имаш много парчета, събери ги в списък и ги долепи наведнъж с join:
parts = ["мляко", "хляб", "яйца"]
print(", ".join(parts))
Изход
мляко, хляб, яйца

Пробвай

Намери грешката

Потребителят въвежда имейла си с главни букви и интервали. Програмата трябва да го изчисти и да го пусне, но казва „Няма такъв потребител.“. На кой ред е грешката?

Избери реда с грешката.

Идеята

Текстът на части

Често текстът съдържа няколко неща, разделени със знак: продукти със запетая, думи с интервал. split го разделя на части и връща списък от текстове:

shopping = "мляко,хляб,яйца,сирене"
items = shopping.split(",")
print(f"Продукти: {len(items)}")
for i in range(len(items)):
print(f"{i + 1}. {items[i]}")
Изход
Продукти: 4
1. мляко
2. хляб
3. яйца
4. сирене

Разделителят не влиза в частите. Оттук нататък работиш с обикновен списък: дължина, индекси, цикъл.

Така се чете и цял ред числа от клавиатурата: разделяш го и превръщаш всяка част в число. Точно това ти липсваше в миналия урок за таблиците:

parts = input().split(" ")
total = 0
for p in parts:
total += int(p)
print(total)
Вход
42 38 51 47 60
Изход
238
По-точно, за любопитните
  • Два интервала един до друг дават празна част '' между тях. split() без аргумент разделя по всякакви интервали и не дава празни части, а пропуска и интервалите отпред и отзад.
  • Обратното на split е ", ".join(items): долепва частите с разделител.
print("а б".split(" "))
print(" а б ".split())
print(", ".join(["а", "б"]))
Изход
['а', '', 'б']
['а', 'б']
а, б

Пробвай

Едно и също ли е?

В Python == сравнява съдържанието на текстовете, буква по буква. Но главната и малката буква са различни символи. Въведено е София:

Какво ще отпечата програмата?

city = input()
print(city == "София")
print(city == "софия")
print(city.lower() == "софия")
Отговор
По-точно, за любопитните

Има и оператор is. Той не сравнява буквите, а пита дали двете страни са един и същ обект в паметта. Текстът, прочетен от клавиатурата, е нов обект, затова при вход admin е така:

user = input()
print(user is "admin")
print(user == "admin")
Изход
False
True

Текст сравнявай винаги с ==.

Упражнение

Имена и думи

Реши задачите в редактора си. Текстът се чете от клавиатурата като цял ред.

1. Името наобратно. Прочети име и го отпечатай отзад напред. Минаваш през индексите от последния към 0 и долепваш символите в нов текст.

Вход
Мария
Изход
яираМ

2. Брой думи. Прочети изречение и преброй думите. Между думите може да има повече от един интервал, а в началото и в края може да има интервали. Подсказка: дума започва там, където символът не е интервал, а предният е интервал (или няма преден).

Вход
Днес е хубав ден
Изход
Думи: 4

3. Палиндром. Палиндромът се чете еднакво отпред назад и отзад напред. Интервалите и главните букви не се броят. Махни интервалите, направи буквите малки и сравни първия символ с последния, втория с предпоследния и т.н.

Вход
Аз обичам мач и боза
Изход
Палиндром е
Вход
Кафе с мляко
Изход
Не е палиндром

Упражнение

Проверки и броене

4. Сигурна парола. Питай за парола, докато не въведат сигурна: поне 8 знака, поне една цифра, една главна и една малка буква. След всяка слаба парола кажи какво се иска. Подсказка: while True с break, когато паролата е сигурна, и проверка на всеки символ с isdigit, isupper, islower.

Вход
kafe
kafe2026
Kafe2026
Изход
Парола:
Слаба парола: поне 8 знака, цифра, главна и малка буква.
Парола:
Слаба парола: поне 8 знака, цифра, главна и малка буква.
Парола:
Паролата е приета.

Още задачи

  • Запетаи вместо интервали. Прочети изречение и замени всеки интервал със запетая. Утре заминаваме за Варна → Утре,заминаваме,за,Варна.
  • Най-дългото име. Прочети имена, разделени със запетая, и отпечатай най-дългото. Ана,Кристина,Петър → Кристина.
  • Гласните със звездичка. Замени всяка гласна (а, ъ, о, у, е, и) със *, независимо дали е главна или малка. Обичам кафе → *б*ч*м к*ф*.
  • Честота на буквите. Преброй колко пъти се среща всяка буква в текста, без значение главна или малка. Подсказка: списък с 32 брояча, по един за всеки символ от 'а' до 'я' (в таблицата със символите между тях има и две руски букви, ы и э, техните броячи просто остават 0). Индексът на буквата c е ord(c) - ord("а"). Отпечатай само буквите, които се срещат.
  • Графика на символите. Като горната задача, но вместо числото отпечатай толкова звездички: а: ****.
  • Колко пъти всяка дума. Прочети изречение и за всяка различна дума отпечатай колко пъти се среща. Подсказка: раздели на думи и за всяка дума провери дали вече не е броена по-рано в списъка.
  • Сортиране по дължина. Прочети думи, разделени с интервал, и ги отпечатай от най-късата към най-дългата. Сортирай списъка както сортираше числа, но сравнявай дължините.
  • Брой изречения. Преброй изреченията в текст: всяко свършва с ., ! или ?. Внимавай с ... и ?!: те завършват едно изречение, не три.
  • Балансирани скоби. Провери дали скобите в текста са балансирани. С един вид скоби стига брояч: ( добавя 1, ) вади 1. Броячът не бива да пада под 0 и накрая трябва да е 0. С три вида, (), [], {}, ти трябва списък от символи, в който пазиш отворените скоби, и брояч докъде е пълен: при затваряща скоба последната отворена трябва да е от същия вид. {[()]} е балансиран, ([)] не е.

Тест

Провери се

1. Какво дава len("Кафе")?
2. Какво прави редът name.upper() сам, без присвояване?
3. Какво дава "Варна" == "варна" в Python?
4. Каква е разликата между 'а' и "а" в Python?
5. Какво дава "Пн,Ср,Пт".split(",")?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·