Exercises: Strings — Text Processing and Manipulation
These exercises progress from foundational drills through applied problems to challenging projects. All code should be Python 3.12+.
Difficulty Guide: - ⭐ Foundational (5-10 min each) - ⭐⭐ Intermediate (10-20 min each) - ⭐⭐⭐ Challenging (20-40 min each) - ⭐⭐⭐⭐ Advanced/Research (40+ min each)
Part A: Indexing, Slicing, and Immutability ⭐
A.1. Given message = "Computational Thinking", what is the value of each expression? Predict first, then verify in Python:
- a) message[0]
- b) message[13]
- c) message[-1]
- d) message[-8]
- e) len(message)
A.2. Given dna = "ATCGATCGATCG", write slice expressions that produce:
- a) "ATCG" (first four characters)
- b) "ATCG" (last four characters)
- c) "GATC" (characters at indices 3-6)
- d) "GCTAGCTA" (the string reversed)
- e) "ACAC" (every third character)
A.3. Explain why the following code produces an error. Then fix it two different ways.
language = "Javon"
language[0] = "P"
language[3] = "h"
print(language) # Intended: "Python"
A.4. Without running the code, predict what each line prints. Then verify.
s = "Hello, World!"
print(s[7:12])
print(s[::-1])
print(s[0:50]) # What happens with out-of-range slice?
print(s[::2])
A.5. Write a function middle_char(s) that returns the middle character of a string. If the string has an even number of characters, return the two middle characters.
print(middle_char("abc")) # Expected: "b"
print(middle_char("abcd")) # Expected: "bc"
print(middle_char("Python")) # Expected: "th"
Part B: String Methods ⭐⭐
B.1. Write a function normalize_name(name) that takes a name in any format and returns it in "Last, First" format with proper capitalization.
print(normalize_name(" alice CHEN ")) # Expected: "Chen, Alice"
print(normalize_name("BOB martinez")) # Expected: "Martinez, Bob"
Hint: Use strip(), split(), title() or capitalize().
B.2. Write a function count_vowels(text) that returns the number of vowels (a, e, i, o, u) in the given text, case-insensitive.
print(count_vowels("Hello, World!")) # Expected: 3
print(count_vowels("AEIOU aeiou")) # Expected: 10
print(count_vowels("rhythm")) # Expected: 0
B.3. Write a function remove_punctuation(text) that removes all punctuation from a string. Punctuation characters to remove: .,!?;:'"()-
print(remove_punctuation("Hello, World!")) # Expected: "Hello World"
print(remove_punctuation("It's a test (really).")) # Expected: "Its a test really"
Hint: Loop through each character and keep only non-punctuation ones, or chain replace() calls.
B.4. Given the following CSV data, write code that splits it into rows and then into fields, printing each person's name and department:
csv_data = """Name,Department,Years
Anika Patel,Biology,12
Carlos Rivera,Chemistry,7
Min-Jun Park,Computer Science,3
Sarah O'Brien,Mathematics,9"""
Expected output:
Anika Patel works in Biology
Carlos Rivera works in Chemistry
Min-Jun Park works in Computer Science
Sarah O'Brien works in Mathematics
B.5. Write a function initials(full_name) that returns a person's initials in uppercase.
print(initials("Anika Patel")) # Expected: "A.P."
print(initials("mary jane watson")) # Expected: "M.J.W."
print(initials(" cher ")) # Expected: "C."
B.6. Write a function censor_word(text, word) that replaces every occurrence of word in text with asterisks of the same length. The censoring should be case-insensitive, but the rest of the text should keep its original case.
print(censor_word("The password is secret", "secret"))
# Expected: "The password is ******"
print(censor_word("Java java JAVA", "java"))
# Expected: "**** **** ****"
Hint: Use lower() for finding, but you'll need to think carefully about replacement.
Part C: Text Processing ⭐⭐
C.1. Write a function pig_latin(word) that converts a word to Pig Latin:
- If the word starts with a vowel, add "yay" to the end
- If the word starts with a consonant, move the first letter to the end and add "ay"
print(pig_latin("apple")) # Expected: "appleyay"
print(pig_latin("hello")) # Expected: "ellohay"
print(pig_latin("python")) # Expected: "ythonpay"
print(pig_latin("is")) # Expected: "isyay"
C.2. Write a function title_case(text) that converts a string to title case, but keeps "small words" (a, an, the, in, on, at, to, for, of, and, but, or) in lowercase unless they're the first word.
print(title_case("the lord of the rings"))
# Expected: "The Lord of the Rings"
print(title_case("a tale of two cities"))
# Expected: "A Tale of Two Cities"
C.3. Write a function word_lengths(sentence) that returns a string showing each word and its length, formatted in aligned columns.
print(word_lengths("The quick brown fox jumps"))
Expected output:
Word Length
---------- ------
The 3
quick 5
brown 5
fox 3
jumps 5
C.4. Write a function extract_emails(text) that finds and returns a list of probable email addresses in a text. For simplicity, assume an email is any word containing exactly one @ symbol.
text = "Contact alice@example.com or bob@university.edu for details. Not an email: @@invalid"
print(extract_emails(text))
# Expected: ['alice@example.com', 'bob@university.edu']
C.5. Write a function gc_content(dna_sequence) that calculates the GC content (percentage of G and C nucleotides) of a DNA sequence. This is one of Dr. Patel's most common calculations.
print(gc_content("ATCGATCG")) # Expected: 50.0
print(gc_content("AAAA")) # Expected: 0.0
print(gc_content("GCGCGC")) # Expected: 100.0
print(gc_content("ATCGAATTCCGG")) # Expected: 50.0
Part D: Input Validation and Formatting ⭐⭐⭐
D.1. Write a function validate_username(username) that checks if a username is valid. Rules:
- 3-20 characters long
- Only letters, digits, and underscores
- Must start with a letter
- Cannot end with an underscore
Return True if valid, False otherwise. Test with at least 6 different usernames (some valid, some not).
D.2. Write a function format_phone(digits) that takes a 10-digit string and returns it in (XXX) XXX-XXXX format. If the input is not exactly 10 digits, return an error message.
print(format_phone("5558675309")) # Expected: "(555) 867-5309"
print(format_phone("123")) # Expected: "Error: expected 10 digits, got 3"
print(format_phone("555abc1234")) # Expected: "Error: input contains non-digit characters"
D.3. Write a program that reads a list of product names and prices, then displays them in a nicely formatted receipt:
items = [
("Wireless Mouse", 29.99),
("USB-C Hub", 49.95),
("Mechanical Keyboard", 149.00),
("Monitor Stand", 79.50),
("Desk Lamp", 34.99),
]
Expected output:
================================
TECH SUPPLIES
================================
Wireless Mouse $ 29.99
USB-C Hub $ 49.95
Mechanical Keyboard $ 149.00
Monitor Stand $ 79.50
Desk Lamp $ 34.99
--------------------------------
TOTAL (5 items) $ 343.43
================================
D.4. Write a function password_strength(password) that evaluates password strength and returns a rating. Check for:
- Length (at least 8 characters)
- Contains uppercase letter
- Contains lowercase letter
- Contains digit
- Contains special character (anything not alphanumeric)
Rating: "weak" (0-1 criteria), "fair" (2-3 criteria), "strong" (4 criteria), "very strong" (all 5).
print(password_strength("hello")) # Expected: "weak"
print(password_strength("Hello123")) # Expected: "fair" or "strong"
print(password_strength("H3llo!World")) # Expected: "very strong"
Part E: Challenges ⭐⭐⭐⭐
E.1. Caesar Cipher. Write two functions: encrypt(text, shift) and decrypt(text, shift) that implement a Caesar cipher. Only shift letters (a-z, A-Z); leave spaces, digits, and punctuation unchanged. Preserve case.
print(encrypt("Hello, World!", 3)) # Expected: "Khoor, Zruog!"
print(decrypt("Khoor, Zruog!", 3)) # Expected: "Hello, World!"
print(encrypt("xyz", 3)) # Expected: "abc" (wraps around)
Hint: Use ord() to get a character's ASCII code and chr() to convert back.
E.2. Run-Length Encoding. Write a function rle_encode(text) that compresses a string using run-length encoding: consecutive identical characters are replaced by the character followed by its count.
print(rle_encode("AAABBBCCDDDDDD")) # Expected: "A3B3C2D6"
print(rle_encode("ABCDE")) # Expected: "A1B1C1D1E1"
print(rle_encode("AABBA")) # Expected: "A2B2A1"
Also write rle_decode(encoded) that reverses the process.
E.3. Text Statistics. Write a function text_stats(text) that returns a formatted report including:
- Total characters (with and without spaces)
- Total words
- Total sentences (count ., !, ?)
- Average word length
- Longest word
- Most common word
Test with a paragraph of text at least 50 words long.
E.4. DNA Complement. In DNA, A pairs with T and C pairs with G. Write a function reverse_complement(dna) that returns the reverse complement of a DNA sequence.
print(reverse_complement("ATCGATCG")) # Expected: "CGATCGAT"
print(reverse_complement("AAACCC")) # Expected: "GGGTTT"
Hint: First create the complement (A<->T, C<->G), then reverse it.
E.5. Formatted Multiplication Table. Write a function multiplication_table(n) that prints a nicely formatted multiplication table from 1 to n, with right-aligned columns.
multiplication_table(5)
Expected output:
| 1 2 3 4 5
-----|-------------------------
1 | 1 2 3 4 5
2 | 2 4 6 8 10
3 | 3 6 9 12 15
4 | 4 8 12 16 20
5 | 5 10 15 20 25