Text Is Everywhere

Think about everything you do on a computer or phone: you type usernames, send messages, create passwords, enter search queries, fill in forms, and read web pages. Every single one of those activities involves text — and in Python, text is stored as strings.

Strings are sequences of characters — letters, digits, spaces, punctuation, even emojis. Python gives you a powerful set of tools to work with them: you can pull out individual characters, extract sections, search for words, change capitalisation, replace parts of a sentence, and much more.

In the real world, string manipulation is everywhere:

By the end of this chapter, you will be able to:

See It in Action

Example 1: String Indexing and Slicing

Every character in a string has a position number called an index. Python starts counting from 0, not 1. You can use these positions to pull out individual characters or “slices” of text. Click Run Code to see how it works:

Python Code
message = "Hello World"

# Indexing - get a single character
print(message[0])     # First character
print(message[6])     # Seventh character (index 6)

# Slicing - get a section of the string
print(message[0:5])   # Characters from index 0 up to (not including) 5
print(message[-5:])   # Last 5 characters

# The full string, backwards
print(message[::-1])
(click "Run Code" to see output)

Notice that message[0:5] gives us Hello — it starts at index 0 and stops before index 5. The slice message[-5:] uses a negative index to count from the end, giving us World. And [::-1] is a neat trick that reverses the entire string!

Try This: Change the message to your own sentence. Can you slice out just one word from it? Try message[6:11] or use negative indices like message[-3:] to grab the last three characters. What happens if you try message[0:5:2]? (That third number is the step.)

Example 2: String Methods

Python strings come with dozens of built-in methods — special functions that you call using a dot after the string. These let you transform text in all sorts of useful ways:

Python Code
text = "  Hello, Python Learners!  "

# Changing case
print(text.upper())          # ALL CAPS
print(text.lower())          # all lowercase
print(text.title())          # Title Case

# Removing whitespace
print(text.strip())          # Remove spaces from both ends

# Replacing text
print(text.replace("Python", "Amazing"))

# Counting and finding
message = "abracadabra"
print(message.count("a"))    # How many times does 'a' appear?
print(message.find("cad"))   # At which index does 'cad' start?
(click "Run Code" to see output)

Each of these methods returns a new string — the original is never changed. The .strip() method is especially useful when processing user input, since people often accidentally add extra spaces. The .find() method returns -1 if the text is not found.

Try This: Try chaining methods together, like text.strip().upper(). What does "hello world".replace("o", "0").replace("l", "1") do? Can you use .count() to count how many times the letter “e” appears in a sentence of your choice?

Example 3: String Checking

Sometimes you don’t want to change a string — you just want to check something about it. Python has methods that return True or False, and you can also use the in keyword to search for text within a string:

Python Code
# Checking what a string starts or ends with
filename = "homework.pdf"
print(filename.startswith("home"))   # True
print(filename.endswith(".pdf"))     # True
print(filename.endswith(".docx"))    # False

# Checking character types
pin = "1234"
word = "Hello"
mixed = "abc123"

print(pin.isdigit())     # True - all characters are digits
print(word.isalpha())    # True - all characters are letters
print(mixed.isalpha())   # False - contains digits too

# Using 'in' to search within a string
sentence = "The quick brown fox jumps over the lazy dog"
print("fox" in sentence)         # True
print("cat" in sentence)         # False
print("fox" not in sentence)     # False
(click "Run Code" to see output)

The in keyword is incredibly useful — it checks whether one string appears anywhere inside another. Combined with if statements, this lets you build things like search features and input validation. The .startswith() and .endswith() methods are perfect for checking file extensions or URL prefixes.

Try This: Try checking if your name contains certain letters using in. What does "12.5".isdigit() return, and why? Test "HELLO".isupper() and "hello".islower(). Can you think of a real situation where .endswith() would be useful?

Example 4: The split() and join() Methods

Two of the most powerful string methods are .split() and .join(). The .split() method breaks a string into a list of smaller strings at a specified separator (called a delimiter). The .join() method does the opposite — it takes a list of strings and glues them back together. These are essential for working with structured data like CSV files:

Python Code
# Splitting a sentence into words (splits on spaces by default)
sentence = "Python is a powerful language"
words = sentence.split()
print(words)
print("There are", len(words), "words")

# Splitting CSV-like data on commas
csv_line = "Alice,17,Computer Science,A*"
fields = csv_line.split(",")
print(fields)
print("Name:", fields[0])
print("Age:", fields[1])
print("Subject:", fields[2])
print("Grade:", fields[3])

# Joining a list back into a string
rejoined = " - ".join(fields)
print(rejoined)

# Practical: reformat comma-separated data as tab-separated
tab_separated = "\t".join(fields)
print(tab_separated)
(click "Run Code" to see output)

The .split() method is your go-to tool for breaking structured text into usable pieces. When called with no argument, it splits on whitespace (spaces, tabs, newlines). When you pass a delimiter like ",", it splits on that exact character. The .join() method works the other way round — the string you call it on becomes the separator between each item in the list.

Try This: Try splitting the string "red;green;blue;yellow" on the semicolon character. What happens if you split "Hello" on the empty string list("Hello")? Use ", ".join(["apples", "bananas", "cherries"]) to create a neat comma-separated list.

Example 5: String Traversal with Loops

Because strings are sequences, you can traverse (loop through) them character by character using a for loop. This is incredibly useful when you need to examine or transform individual characters — for example, building up a new modified string one piece at a time:

Python Code
# Simple traversal: print each character
word = "Python"
for char in word:
    print(char, end=" ")
print()  # New line

# Building a new string: remove all vowels
original = "String Manipulation"
result = ""
for char in original:
    if char.lower() not in "aeiou":
        result = result + char
print("Without vowels:", result)

# Building a new string: add a dash between each character
spaced = ""
for i in range(len(original)):
    spaced = spaced + original[i]
    if i < len(original) - 1:
        spaced = spaced + "-"
print("Dashed:", spaced)

# Count different character types
text = "Hello World 123!"
letters = 0
digits = 0
spaces = 0
other = 0
for char in text:
    if char.isalpha():
        letters += 1
    elif char.isdigit():
        digits += 1
    elif char == " ":
        spaces += 1
    else:
        other += 1
print(f"Letters: {letters}, Digits: {digits}, Spaces: {spaces}, Other: {other}")
(click "Run Code" to see output)

The key technique here is building up a new string. Because strings are immutable (you cannot change them in place), we start with an empty string "" and add characters to it one at a time using concatenation (+). This pattern — loop, check, build — is one of the most common in all of programming.

Try This: Write a loop that takes a string and builds a new version where every other character is uppercase. For example, "hello" becomes "hElLo". Hint: use range(len(text)) and check whether the index is odd or even with i % 2.

Example 6: Practical — Email Validator

Let us put several string techniques together to build a practical email address validator. A valid email needs certain features: it must contain an @ symbol, have text before and after the @, and end with a recognised domain. This is the kind of validation that happens every time you sign up for a website:

Python Code
def validate_email(email):
    """Check if an email address is valid."""
    # Remove any accidental whitespace
    email = email.strip()

    # Check 1: Must contain exactly one @ symbol
    if email.count("@") != 1:
        return "Invalid: must contain exactly one @ symbol"

    # Check 2: Split into local part and domain
    parts = email.split("@")
    local = parts[0]
    domain = parts[1]

    # Check 3: Local part must not be empty
    if len(local) == 0:
        return "Invalid: nothing before the @ symbol"

    # Check 4: Domain must not be empty
    if len(domain) == 0:
        return "Invalid: nothing after the @ symbol"

    # Check 5: Domain must contain a dot
    if "." not in domain:
        return "Invalid: domain must contain a dot (e.g. .com, .co.uk)"

    # Check 6: Must end with a valid domain extension
    valid_endings = [".com", ".org", ".co.uk", ".net", ".edu", ".ac.uk"]
    has_valid_ending = False
    for ending in valid_endings:
        if email.endswith(ending):
            has_valid_ending = True
    if not has_valid_ending:
        return "Invalid: unrecognised domain extension"

    return "Valid email address"

# Test with different email addresses
test_emails = [
    "student@school.co.uk",
    "alice@gmail.com",
    "missing-at-sign.com",
    "@nodomain.com",
    "user@",
    "double@@at.com",
    "bob@website.xyz"
]

for email in test_emails:
    result = validate_email(email)
    print(f"{email:30s} -> {result}")
(click "Run Code" to see output)

This example combines many of the skills from this chapter: .strip() to clean input, .count() to check for the right number of @ symbols, .split() to separate the parts, len() to check for empty sections, in to check for a dot, and .endswith() to validate the domain. Real-world email validation is even more complex, but this covers the essentials.

Try This: Add your own email address to the test list. Can you add an extra check that the local part (before the @) contains only letters, numbers, dots, and underscores? Try adding a check that the email does not contain any spaces.

How Does It Work?

Strings Are Sequences

A string in Python is a sequence of characters, much like a list is a sequence of items. Each character has a numbered position (an index), starting at 0 for the first character. This means you can access any character by its position, loop through every character one at a time, and use slicing to extract sections.

For the string "Python", the indices look like this:

Character P y t h o n
Index 0 1 2 3 4 5
Negative index -6 -5 -4 -3 -2 -1

Negative indices count backwards from the end. So "Python"[-1] gives "n" (the last character), and "Python"[-2] gives "o".

Indexing and Slicing Syntax

The full slicing syntax is string[start:stop:step]:

Some useful examples:

Expression Result Explanation
"Python"[0:3] "Pyt" Characters at index 0, 1, 2
"Python"[2:] "thon" From index 2 to the end
"Python"[:4] "Pyth" From the start up to index 4
"Python"[::2] "Pto" Every second character
"Python"[::-1] "nohtyP" The whole string reversed

Strings Are Immutable

One of the most important things to understand about strings is that they are immutable — you cannot change them after they are created. When you call a method like .upper() or .replace(), Python does not modify the original string. Instead, it creates and returns a brand new string. If you want to keep the result, you must store it in a variable:

Python Code
greeting = "hello"

# This does NOT change greeting
greeting.upper()
print(greeting)        # Still "hello"!

# You must save the result
greeting = greeting.upper()
print(greeting)        # Now "HELLO"
(click "Run Code" to see output)
Key Concept: Strings Are Immutable String methods never change the original string. They always return a new string. If you want to use the result, you must assign it to a variable — for example, name = name.upper(). This is one of the most common mistakes beginners make!

Common String Methods Summary

Method What It Does Example
.upper() Returns the string in uppercase "hi".upper()"HI"
.lower() Returns the string in lowercase "HI".lower()"hi"
.title() Capitalises the first letter of each word "hi there".title()"Hi There"
.strip() Removes leading and trailing whitespace " hi ".strip()"hi"
.replace(old, new) Replaces all occurrences of old with new "hello".replace("l", "r")"herro"
.count(sub) Counts how many times sub appears "banana".count("a")3
.find(sub) Returns the index of the first occurrence, or -1 "hello".find("ll")2
.split(sep) Splits the string into a list at each sep "a,b,c".split(",")["a","b","c"]
.join(list) Joins a list of strings using the string as separator "-".join(["a","b"])"a-b"
.startswith(s) Checks if the string begins with s "hello".startswith("he")True
.endswith(s) Checks if the string ends with s "hello".endswith("lo")True
.isdigit() Returns True if all characters are digits "42".isdigit()True
.isalpha() Returns True if all characters are letters "hi".isalpha()True
Did You Know? The most commonly used string method in real-world Python code is .strip(). Almost every program that reads user input or data from a file uses it to remove accidental whitespace. When a user types " hello " into a form, .strip() cleans it up to just "hello". There are also .lstrip() and .rstrip() for removing whitespace from only the left or right side.

ASCII and Character Codes

Computers do not actually store letters — they store numbers. Every character you see on screen has a corresponding numeric code. The original system for this is called ASCII (American Standard Code for Information Interchange), which assigns a number from 0 to 127 to each character. For example:

Character ASCII Code Character ASCII Code
A 65 a 97
B 66 b 98
Z 90 z 122
0 48 9 57
(space) 32 ! 33

Python gives you two built-in functions to work with character codes:

Python Code
# Convert characters to their ASCII codes
print("A =", ord("A"))
print("a =", ord("a"))
print("0 =", ord("0"))
print("! =", ord("!"))

# Convert ASCII codes back to characters
print("65 =", chr(65))
print("97 =", chr(97))
print("48 =", chr(48))

# This is why alphabetical sorting works!
# 'A' (65) < 'B' (66) < 'C' (67) ...
print("Is 'A' less than 'B'?", "A" < "B")    # True
print("Is 'a' less than 'Z'?", "a" < "Z")    # False! (97 > 90)

# Print the entire uppercase alphabet using chr()
alphabet = ""
for code in range(65, 91):
    alphabet += chr(code)
print("Alphabet:", alphabet)
(click "Run Code" to see output)

Understanding character codes explains why string comparison works alphabetically. When Python compares "apple" < "banana", it compares the ASCII codes character by character. Since ord("a") is 97 and ord("b") is 98, "apple" comes first. However, be careful: uppercase letters (65–90) have lower codes than lowercase letters (97–122), so "Z" < "a" is True! This is why you often need to convert to the same case before comparing.

Did You Know? ASCII (American Standard Code for Information Interchange) was created in the 1960s and only covers 128 characters — basically just English letters, digits, and punctuation. Modern Python uses Unicode, which can represent over 149,000 characters from every writing system in the world, including Chinese, Arabic, Hindi, and even emoji! That is why ord("🐍") returns 128013 and len("Hello 🌎") counts the globe emoji as a single character. Unicode is a superset of ASCII — the first 128 Unicode codes are identical to ASCII.
Did You Know? Strings in Python are stored as Unicode, meaning they can represent every character in every language. A Python string can contain English, Japanese, Arabic, and emoji all at once: "Hello こんにちは مرحبا 👋" is a perfectly valid Python string. Each character has a unique code point — ord("A") returns 65, but ord("こ") returns 12371!
Watch out: A very common misconception is thinking that .replace() changes the original string. It does not. For example, after running name = "Alice" followed by name.replace("A", "a"), the variable name is still "Alice". You must write name = name.replace("A", "a") to save the change.
Watch out: Remember that string indexing starts at 0, not 1. The first character of "Hello" is "Hello"[0], which is "H". Trying to access an index beyond the length of the string (e.g. "Hello"[10]) will cause an IndexError.

Worked Example: Building a Caesar Cipher

The Problem

A Caesar cipher is one of the oldest encryption techniques, supposedly used by Julius Caesar to send secret messages. It works by shifting each letter in the message by a fixed number of positions in the alphabet. For example, with a shift of 3:

  • A becomes D
  • B becomes E
  • X becomes A (it wraps around!)
  • Z becomes C

This is a classic GCSE exam question. Let us build it step by step using ord(), chr(), and modular arithmetic.

Step 1: Understand the character codes

Uppercase letters run from ord("A") = 65 to ord("Z") = 90. To shift a letter, we need to convert it to a number (0–25), add the shift, wrap around using the modulo operator (%), and convert back to a letter.

Step 2: Work through an example by hand

To encrypt the letter "H" with a shift of 3:

  • Get the ASCII code: ord("H") = 72
  • Subtract 65 to get a position (0–25): 72 - 65 = 7
  • Add the shift: 7 + 3 = 10
  • Use modulo to wrap around: 10 % 26 = 10
  • Add 65 back to get the ASCII code: 10 + 65 = 75
  • Convert back to a character: chr(75) = "K"

So "H" becomes "K".

Step 3: Handle wrapping around the alphabet

The modulo operator (%) is the key to wrapping. For example, "Y" with a shift of 3:

  • Position: ord("Y") - 65 = 24
  • Shifted: 24 + 3 = 27
  • Wrapped: 27 % 26 = 1
  • Result: chr(1 + 65) = "B"

Without the modulo, we would get code 92 which is not a letter!

Step 4: Write the complete solution

We loop through each character, shift letters, and leave non-letter characters (spaces, punctuation) unchanged.

Python Code
def caesar_cipher(message, shift):
    """Encrypt a message using a Caesar cipher."""
    result = ""

    for char in message:
        if char.isalpha():
            # Determine if uppercase or lowercase
            if char.isupper():
                base = ord("A")
            else:
                base = ord("a")

            # Shift the character
            position = ord(char) - base         # 0-25
            shifted = (position + shift) % 26   # Wrap around
            new_char = chr(shifted + base)       # Back to a letter
            result += new_char
        else:
            # Keep spaces, punctuation, digits unchanged
            result += char

    return result

# Encrypt a message with a shift of 3
original = "HELLO WORLD"
encrypted = caesar_cipher(original, 3)
print("Original: ", original)
print("Encrypted:", encrypted)

# Decrypt by shifting in the opposite direction
decrypted = caesar_cipher(encrypted, -3)
print("Decrypted:", decrypted)

# Try with mixed case and punctuation
secret = "Attack at dawn!"
coded = caesar_cipher(secret, 5)
print("\nOriginal: ", secret)
print("Encrypted:", coded)
print("Decrypted:", caesar_cipher(coded, -5))
(click "Run Code" to see output)
Key Concept: Modular Arithmetic The modulo operator (%) gives you the remainder after division. 27 % 26 = 1 because 27 divided by 26 leaves a remainder of 1. This is perfect for “wrapping around” — when you go past Z, you loop back to A. You will encounter modular arithmetic in many computing contexts, including hash functions, clock arithmetic, and cryptography.

Real-World Applications

Where Is String Manipulation Used?

Data Cleaning

When organisations collect data from forms, surveys, or imports, it is often messy. Names might be typed as " jOHN sMITH " instead of "John Smith". Data cleaning scripts use .strip() to remove extra whitespace, .title() or .capitalize() to fix capitalisation, and .replace() to standardise formats. Databases with millions of records rely on these operations to keep data consistent.

Natural Language Processing (NLP)

NLP is the field of computer science that teaches computers to understand human language. Applications like voice assistants (Siri, Alexa), translation services, and chatbots all start by breaking text into words using .split(), converting to lowercase with .lower(), and removing punctuation. Sentiment analysis programs use these techniques to determine whether a product review is positive or negative by checking for key words.

Cybersecurity

One of the most common attacks on websites is injection — where an attacker types malicious code into a form field hoping it will be executed. String validation is the first line of defence. Checking that usernames contain only safe characters, that inputs do not contain <script> tags, and that data matches expected patterns (using methods like .isalnum(), .find(), and in) helps prevent these attacks.

File Processing

Programs that read data files — especially CSV (Comma-Separated Values) files — rely heavily on .split(",") to break each line into fields, .strip() to clean up whitespace, and type conversion to turn string values into numbers. Scientific research, business reporting, and government statistics all depend on these string-processing techniques.

Search Engines

When you type a query into Google, the search engine tokenises your input — splitting it into individual words using .split(), converting to lowercase, removing common words like “the” and “is” (called stop words), and sometimes stemming words (reducing “running”, “runs”, and “ran” to the root word “run”). All of this is fundamentally string manipulation.

Common Mistakes to Avoid

Watch Out for These Pitfalls

1. Forgetting that strings are immutable

This is the number one mistake beginners make. String methods return new strings — they do not change the original. If you write name.upper() without saving the result, nothing changes:

name = "alice"
name.upper()     # This does NOTHING to name!
name = name.upper()  # This is correct

2. Off-by-one errors in slicing

Remember that the stop index in a slice is not included. If you want characters at indices 0, 1, 2, 3, and 4, you write text[0:5], not text[0:4]. A useful rule: the number of characters in a slice is stop - start.

3. Case sensitivity when comparing strings

In Python, "Hello" and "hello" are not the same string. If you are comparing user input, always convert both sides to the same case first:

if user_input.lower() == "yes":  # Handles "Yes", "YES", "yes", etc.

4. Confusing .find() returning -1 with an error

When .find() cannot locate the substring, it returns -1 — it does not raise an error. This can cause bugs if you use the result as an index without checking. Always test the return value:

pos = text.find("hello")
if pos != -1:     # Found it!
    print("Found at index", pos)

If you want Python to raise an error when the substring is not found, use .index() instead of .find().

Key Vocabulary

Term Definition
String A sequence of characters (text data), written in quotes in Python. For example, "Hello".
Character A single letter, digit, symbol, or space. In Python, a character is simply a string of length 1.
Index The position number of a character within a string. Python uses zero-based indexing (the first character is at index 0).
Slice A portion of a string extracted using the [start:stop:step] syntax.
Substring A smaller string that appears within a larger string. For example, "ell" is a substring of "Hello".
Immutable Cannot be changed after creation. Strings in Python are immutable — methods return new strings rather than modifying the original.
Concatenation Joining two or more strings together using the + operator. For example, "Hello" + " " + "World".
Method A function that belongs to an object, called using dot notation. For example, "hello".upper() calls the upper method on the string.
Traverse To iterate (loop) through a sequence one element at a time. For example, for char in "hello": traverses each character.
ASCII American Standard Code for Information Interchange — a system that assigns a numeric code (0–127) to each character. ord("A") returns 65.
Unicode A modern character encoding standard that covers over 149,000 characters from all the world's writing systems. Python uses Unicode internally.
Delimiter A character or string used to separate pieces of data. In CSV files, the delimiter is a comma. In .split(","), the comma is the delimiter.
split() A string method that breaks a string into a list of substrings at each occurrence of a delimiter.
join() A string method that concatenates a list of strings, inserting the string between each element.
strip() A string method that removes leading and trailing whitespace (or specified characters) from a string.

Your Turn

Exercise 1: Case Converter
Guided

Take the sentence below and print it in three different forms: uppercase, lowercase, and title case. The code is mostly written for you — just fill in the blanks with the correct string methods.

Pseudocode:
STEP 1: Store a sentence in a variable
STEP 2: Print the sentence converted to uppercase
STEP 3: Print the sentence converted to lowercase
STEP 4: Print the sentence converted to title case
Your Code
sentence = "the quick brown fox jumps over the lazy dog"

# Print in uppercase (replace ____ with the correct method)
print(sentence.____())

# Print in lowercase
print(sentence.____())

# Print in title case
print(sentence.____())
(click "Run Code" to see output)
Need a hint?

The three methods you need are: .upper() for all capitals, .lower() for all lowercase, and .title() for capitalising the first letter of each word.

Your completed code should look like:

print(sentence.upper())

print(sentence.lower())

print(sentence.title())

Exercise 2: Vowel Counter
Partially Guided

Write a program that counts how many vowels (a, e, i, o, u) are in a given word. You will need to loop through each character and check whether it is a vowel.

Pseudocode:
STEP 1: Store a word in a variable
STEP 2: Convert the word to lowercase (so “A” and “a” both count)
STEP 3: Create a counter variable, starting at 0
STEP 4: Loop through each character in the word
STEP 5: If the character is in “aeiou”, add 1 to the counter
STEP 6: Print the total number of vowels
Your Code
word = "Programming"
word_lower = word.lower()
count = 0

for char in word_lower:
    if char in "aeiou":
        # Add 1 to the count
        pass  # Replace this line

print("The word '" + word + "' has", count, "vowels.")
(click "Run Code" to see output)
Need a hint?

Replace the pass line with count = count + 1 (or the shorthand count += 1). This increases the counter by 1 each time a vowel is found.

The word “Programming” should have 3 vowels: o, a, i.

Try changing the word to your own name or a longer sentence and see if the count is correct!

Exercise 3: Text Processor
Open-Ended

Create a simple text processor that performs at least two of the following tasks on a sentence of your choice. Be creative!

  • Word censor: Replace a specific word with asterisks (e.g. replace “bad” with “***”)
  • String reverser: Print the sentence backwards
  • Acronym maker: Take the first letter of each word to form an acronym (e.g. “as soon as possible” → “ASAP”)
  • Character report: Count letters, digits, and spaces separately
Pseudocode:
STEP 1: Store a sentence in a variable
STEP 2: Choose at least two of the tasks above
STEP 3: Use string methods, slicing, and/or loops to complete each task
STEP 4: Print the results with clear labels so the output is easy to read
Your Code
# Text Processor
# Choose a sentence and perform at least two tasks on it

sentence = "Python Is Super Cool"

# Task 1: Censor a word
# (replace a word with asterisks)

# Task 2: Reverse the string
# (use slicing to reverse it)

# Task 3: Create an acronym
# (take the first letter of each word)

# Print your results!
(click "Run Code" to see output)
Need a hint?

Here are some approaches for each task:

  • Censor: Use sentence.replace("Cool", "****")
  • Reverse: Use sentence[::-1]
  • Acronym: Use sentence.split() to get a list of words, then loop through them and take word[0] from each. Build up the acronym by adding each first letter to an empty string.
  • Character report: Loop through each character and use .isalpha(), .isdigit(), and check for " " to count each type.

Here is an example that does all three:

censored = sentence.replace("Cool", "****")

reversed_text = sentence[::-1]

acronym = ""

for word in sentence.split():

    acronym = acronym + word[0]

print("Censored:", censored)

print("Reversed:", reversed_text)

print("Acronym:", acronym.upper())

Exercise 4: Palindrome Checker
Partially Guided

A palindrome is a word or phrase that reads the same forwards and backwards. For example, “racecar”, “madam”, and “level” are all palindromes. Write a program that checks whether a word is a palindrome.

Pseudocode:
STEP 1: Store a word in a variable
STEP 2: Convert the word to lowercase (so “Racecar” and “racecar” both work)
STEP 3: Reverse the lowercase word using slicing
STEP 4: Compare the lowercase word to its reverse
STEP 5: If they are the same, print that it is a palindrome
STEP 6: Otherwise, print that it is not a palindrome
Your Code
# Palindrome Checker
word = "Racecar"

# Step 1: Convert to lowercase
word_lower = word.lower()

# Step 2: Reverse the string (use slicing!)
reversed_word = ____  # Replace ____ with the reversed version

# Step 3: Compare and print the result
if word_lower == reversed_word:
    print(f"'{word}' is a palindrome!")
else:
    print(f"'{word}' is NOT a palindrome.")

# Bonus: Test with several words
test_words = ["madam", "python", "level", "hello", "kayak"]
for w in test_words:
    if w == w[::-1]:
        print(f"  {w} -> palindrome")
    else:
        print(f"  {w} -> not a palindrome")
(click "Run Code" to see output)
Need a hint?

Replace ____ with word_lower[::-1]. This uses Python’s slice syntax with a step of -1 to reverse the string.

Challenge extension: Can you make the palindrome checker work for phrases too? For example, “A man a plan a canal Panama” is a palindrome if you ignore spaces and capitalisation. Hint: use .replace(" ", "") to remove spaces before checking.

Exercise 5: Password Strength Checker
Open-Ended

Write a program that checks the strength of a password. A strong password should meet all of the following criteria:

  • At least 8 characters long
  • Contains at least one uppercase letter
  • Contains at least one lowercase letter
  • Contains at least one digit
  • Contains at least one special character (e.g. !@#$%^&*)
Pseudocode:
STEP 1: Store a password in a variable
STEP 2: Create boolean variables for each check, initially set to False
STEP 3: Check the length is at least 8
STEP 4: Loop through each character in the password
STEP 5: For each character, check if it is uppercase, lowercase, a digit, or special
STEP 6: After the loop, report which criteria are met and which are missing
STEP 7: If all criteria are met, print that the password is strong
Your Code
# Password Strength Checker
password = "MyP@ssw0rd"

# Define the special characters to check for
special_chars = "!@#$%^&*()-_=+[]{}|;:',.<>?/"

# Track which criteria are met
has_length = len(password) >= 8
has_upper = False
has_lower = False
has_digit = False
has_special = False

# Loop through each character and check
for char in password:
    # Add your checks here
    pass

# Report results
print(f"Password: {password}")
print(f"  Length >= 8:      {has_length}")
print(f"  Has uppercase:    {has_upper}")
print(f"  Has lowercase:    {has_lower}")
print(f"  Has digit:        {has_digit}")
print(f"  Has special char: {has_special}")

if has_length and has_upper and has_lower and has_digit and has_special:
    print("\nPassword is STRONG!")
else:
    print("\nPassword is WEAK. Please fix the issues above.")
(click "Run Code" to see output)
Need a hint?

Replace the pass line inside the loop with checks for each character type:

if char.isupper():
    has_upper = True
elif char.islower():
    has_lower = True
elif char.isdigit():
    has_digit = True
elif char in special_chars:
    has_special = True

Test with passwords like "hello" (too weak), "Hello123" (missing special character), and "MyP@ssw0rd" (strong).

Chapter Summary

What You Have Learnt

  • Strings are sequences of characters, accessed by index starting from 0. Negative indices count from the end.
  • Slicing uses the syntax [start:stop:step] to extract portions of a string. The stop index is not included.
  • Strings are immutable — methods like .upper(), .replace(), and .strip() return new strings; they never modify the original.
  • String methods let you change case (.upper(), .lower(), .title()), search (.find(), .count(), in), check properties (.isdigit(), .isalpha(), .startswith()), and clean text (.strip(), .replace()).
  • split() and join() convert between strings and lists — essential for processing structured data like CSV.
  • String traversal using for char in string: lets you examine or transform each character individually.
  • ASCII and Unicode assign numeric codes to characters. The ord() and chr() functions convert between characters and their codes.
  • The Caesar cipher demonstrates how ord(), chr(), and modular arithmetic can be used for encryption — a classic GCSE topic.
  • Practical applications include email validation, password checking, data cleaning, and text analysis.

Think About It

Take a moment to think about what you have learnt in this chapter. Try to answer these questions in your head or discuss them with a classmate:

Exam-Style Questions

Practise answering these questions as you would in a GCSE Computer Science exam.

[2 marks]

Question 1: What does it mean for strings to be ‘immutable’ in Python? Give an example to illustrate your answer.

[2 marks]

Question 2: Explain the difference between .find() and the in keyword when searching for text within a string. When might you choose one over the other?

[4 marks]

Question 3: Write a Python function called count_types that takes a string as a parameter and returns the number of uppercase letters, lowercase letters, and digits it contains. For example, count_types("Hello World 123") should print: Uppercase: 2, Lowercase: 8, Digits: 3.

Exam question model answers

Question 1 (2 marks): Immutable means the string cannot be changed after it has been created (1 mark). For example, calling name.upper() does not change name — it returns a new string, so you must write name = name.upper() to store the result (1 mark).

Question 2 (2 marks): The in keyword returns True or False to indicate whether the substring exists (1 mark). The .find() method returns the index (position) where the substring was found, or -1 if it was not found (1 mark). You would use in when you only need to know if the text is present, and .find() when you need to know where it is.

Question 3 (4 marks):

def count_types(text): (1 mark for function definition with parameter)
    upper = 0
    lower = 0
    digits = 0 (1 mark for initialising counters)
    for char in text: (1 mark for loop and correct checks)
        if char.isupper():
            upper += 1
        elif char.islower():
            lower += 1
        elif char.isdigit():
            digits += 1
    print(f"Uppercase: {upper}, Lowercase: {lower}, Digits: {digits}") (1 mark for correct output)

Extra Resources

Keep practising your string skills:

What’s Next?

No matter how carefully you write your code, things can go wrong: a user might type a word when you expected a number, a file might not exist, or a calculation might try to divide by zero. In Chapter 10: Error Handling, you will learn how to anticipate these problems and handle them gracefully using try and except blocks — so your programs do not just crash but instead respond helpfully when something unexpected happens.