Working with text
Problem
The code is right, but it does not work
The shop gives 10% off with the code SUMMER10. A customer copies it from the ad, but types it in lowercase and puts a space in front by accident:
code = input()if code == "SUMMER10": print("10% off")else: print("Invalid code") summer10Invalid codeTo a person, the code is the same. To the program, summer10 and SUMMER10 are different texts: the first has one more character, and the letters are different. You need to be able to look inside a text and fix it.
The idea
Text is a sequence of characters
Text is characters placed one after another: letters, digits, spaces, signs. Each character has an index, like in an array. The first one has index 0.
code = "SUMMER10"print(len(code))print(code[0])print(code[len(code) - 1])8S0len(code)is the number of characters.code[0]is the character with index 0. The last character has indexlen(code) - 1.
There is no separate type for a single character here (in Java and C# it is called char). A character is simply a text of length 1. Single and double quotes do the same thing: 'S' and "S" are the same text.
code = "SUMMER10"print(code[-1])print(type(code[0]))print(len(code[0]))print('S' == "S")0<class 'str'>1Truecode[-1] is the last character: a negative index counts from the back, -1 is the last one, -2 the one before it. This is shorter than code[len(code) - 1].
len gives the length of both text and lists.
See it
Character by character
How many digits are in the phone number? You loop through the indexes from 0 to the last one and look at each character:
phone = "0888 123 456"digits = 0for i in range(len(phone)): c = phone[i] if c >= "0" and c <= "9": digits += 1print(f"Digits: {digits}")Digits: 10c >= "0" and c <= "9" works because every character has a code: a number in the character table. Texts are compared by the codes of their characters. The digits from "0" to "9" have consecutive codes, and so do the letters from "a" to "z".
More precisely, if you are curious
- There are ready-made methods for checks like these:
c.isdigit(),c.isalpha(),c.isupper(),c.islower(). They also know letters outside English, likeé. ord("A")gives the code of a character, that is, 65. The other way,chr(66)is"B". Text and a number don't add up:"a" + 1stops the program withTypeError.- Without an index:
for c in phone:goes straight through the characters of the text.
print("7".isdigit(), "é".isalpha(), "É".isupper(), "é".islower())print(ord("A"), chr(66))try: print("a" + 1)except TypeError as e: print(f"TypeError: {e}")digits = 0for c in "0888 123 456": if c.isdigit(): digits += 1print(digits)True True True True65 BTypeError: can only concatenate str (not "int") to str10Try it
Which character?
What will the program print?
word = "Invoice"print(word[3])print(len(word))The indexes are I 0, n 1, v 2, o 3. The character with index 3 is the fourth one: "o". The length is the count of all the characters, 7. The last index is 6.
The idea
The methods of text
Text comes with ready-made methods: actions you call with a dot after the text. Here is the fix for the promo code:
code = " summer10"clean = code.strip().upper()print(f"[{clean}]")print(clean == "SUMMER10")[SUMMER10]Truestrip() removes the spaces at the front and at the back. upper() makes all the letters uppercase. Methods can be chained one after another: the first returns a text, and the second is called on it.
A few more, on an email address:
email = "ivan.petrov@mail.bg"print("@" in email)print(email.find("@"))print(email[0:email.find("@")])print(email.replace(".", " "))print(email.endswith(".bg"))True11ivan.petrovivan petrov@mail bgTrue| Method | What it does |
|---|---|
x in email |
whether x is inside |
find(x) |
the index where x first appears (-1 if it is not there) |
email[a:b] |
a part of the text |
replace(x, y) |
every x is replaced with y |
startswith(x), endswith(x) |
whether it starts (ends) with x |
lower() |
all letters lowercase |
"@" in email is not a method but an operator: in asks whether the text on the left appears in the text on the right. You take a part of the text with square brackets and a colon, email[a:b]; there is more on that in "More precisely" below.
print("ivan.petrov@mail.bg".find("#"))print("Cafe".startswith("C"))print("Cafe".lower())-1TruecafeMore precisely, if you are curious
date[a:b] takes the characters from index a to index b, without b. Their count is b - a. Without an end, date[a:], it takes everything to the end. A negative index counts from the back: date[-2:] is the last two characters.
date = "2026-10-24"print(date[5:7])print(date[8:])print(date[-2:])102424The idea
Text does not change
There is one quirk that confuses everyone at first:
name = "maria"name.upper()print(name)name = name.upper()print(name)mariaMARIAThe second line does nothing you can see. upper() does not change name. It makes a new text in uppercase and returns it. Nobody stores it, and it is lost.
Once created, a text cannot be changed. It is called immutable. Every method that "changes" a text actually returns a new one. To keep it, you assign it: name = name.upper().
name = "maria"name.upper()print(name)name = name.upper()print(name)Step by step, in words
- name holds the text "maria": 5 characters with indexes from 0 to 4.
- upper() does not touch "maria". It makes a new text, "MARIA".
- Nobody stores the new text, and it is lost.
- name still holds "maria", and that is what gets printed.
- Another new text, "MARIA", but now = stores it in name. The old "maria" is dropped.
- Now name holds "MARIA".
More precisely, if you are curious
- You cannot change a character inside a text with an index either:
name[0] = "M"stops the program withTypeError: 'str' object does not support item assignment. You make a new text, for example"M" + name[1:]. - When you have many pieces, collect them in a list and join them all at once with
join:
parts = ["milk", "bread", "eggs"]print(", ".join(parts))milk, bread, eggsTry it
Find the bug
The user types their email with uppercase letters and spaces. The program should clean it up and let them in, but it says "No such user.". Which line has the bug?
Pick the line with the bug.
email.strip() returns a new text without the spaces, but it is not stored anywhere. email still has the spaces. The fix is email = email.strip(), just like on line 2.
The idea
Text in parts
Often a text holds several things, separated by a sign: items with a comma, words with a space. split splits it into parts and returns a list of texts:
shopping = "milk,bread,eggs,cheese"items = shopping.split(",")print(f"Items: {len(items)}")for i in range(len(items)): print(f"{i + 1}. {items[i]}")Items: 41. milk2. bread3. eggs4. cheeseThe separator is not part of the pieces. From here on you work with a regular list: length, indexes, a loop.
This is also how you read a whole line of numbers from the keyboard: you split it and turn each part into a number. That is exactly what you were missing in the last lesson, on tables:
parts = input().split(" ")total = 0for p in parts: total += int(p)print(total)42 38 51 47 60238More precisely, if you are curious
- Two spaces next to each other give an empty part
''between them.split()with no argument splits on any whitespace and gives no empty parts, and it also skips the spaces at the front and at the back. - The opposite of
splitis", ".join(items): it joins the parts with a separator.
print("a b".split(" "))print(" a b ".split())print(", ".join(["a", "b"]))['a', '', 'b']['a', 'b']a, bTry it
Is it the same?
In Python, == compares the content of texts, letter by letter. But an uppercase and a lowercase letter are different characters. The input is Sofia:
What will the program print?
city = input()print(city == "Sofia")print(city == "sofia")print(city.lower() == "sofia")city == "Sofia" is True: the letters are the same. "sofia" starts with a lowercase "s", which is a different character, so it is False. If uppercase and lowercase don't matter, compare both texts in lowercase: city.lower() == "sofia".
More precisely, if you are curious
There is also the operator is. It does not compare the letters: it asks whether both sides are one and the same object in memory. The text read from the keyboard is a new object, so with the input admin you get this:
user = input()print(user is "admin")print(user == "admin")FalseTrueAlways compare text with ==.
Exercise
Names and words
Solve the exercises in your editor. The text is read from the keyboard as a whole line.
1. The name backward. Read a name and print it from back to front. You go through the indexes from the last one down to 0 and append the characters to a new text.
MariaairaM2. Word count. Read a sentence and count the words. There can be more than one space between words, and there can be spaces at the start and at the end. Hint: a word starts where the character is not a space and the one before it is a space (or there is none before it).
Today is a holidayWords: 43. Palindrome. A palindrome reads the same from front to back and from back to front. Spaces and uppercase letters don't count. Remove the spaces, make the letters lowercase, and compare the first character with the last, the second with the one before last, and so on.
Never odd or evenIt is a palindromeCoffee with milkNot a palindromeExercise
Checks and counting
4. Strong password. Ask for a password until the user enters a strong one: at least 8 characters, at least one digit, one uppercase and one lowercase letter. After each weak password, say what is required. Hint: while True with break when the password is strong, and a check of each character with isdigit, isupper, islower.
coffeecoffee2026Coffee2026Password:Weak password: at least 8 characters, a digit, an uppercase and a lowercase letter.Password:Weak password: at least 8 characters, a digit, an uppercase and a lowercase letter.Password:Password accepted.More exercises
- Commas instead of spaces. Read a sentence and replace every space with a comma.
Tomorrow we leave for Varna→Tomorrow,we,leave,for,Varna. - The longest name. Read names separated by commas and print the longest one.
Ana,Kristina,Peter→Kristina. - Vowels as stars. Replace every vowel (a, e, i, o, u) with
*, whether it is uppercase or lowercase.I love coffee→* l*v* c*ff**. - Letter frequency. Count how many times each letter appears in the text, uppercase or lowercase alike. Hint: a list of 26 counters, one for each letter from
'a'to'z'. The index of the lettercisord(c) - ord("a"). Print only the letters that appear. - Character chart. Like the exercise above, but instead of the number, print that many stars:
a: ****. - How many times each word. Read a sentence and for each different word print how many times it appears. Hint: split it into words, and for each word check whether it was already counted earlier in the list.
- Sorting by length. Read words separated by spaces and print them from the shortest to the longest. Sort the list the way you sorted numbers, but compare the lengths.
- Sentence count. Count the sentences in a text: each one ends with
.,!or?. Watch out for...and?!: they end one sentence, not three. - Balanced brackets. Check whether the brackets in a text are balanced. With one kind of bracket, a counter is enough:
(adds 1,)subtracts 1. The counter must never drop below 0, and at the end it must be 0. With three kinds,(),[],{}, you need a list of characters in which you keep the open brackets, and a counter of how full it is: at a closing bracket, the last open one must be of the same kind.{[()]}is balanced,([)]is not.
Test