gl/ripetizioni

Strings and files in Python: exercises for your test

Strings, slicing, methods and reading files in Python for Year 3: three exercises with complete code and output, plus the mistakes to avoid.

by Gaetano Livornese

Read the original in Italian

If you study computer science in Year 3 of the applied-sciences option of the liceo scientifico, with two hours a week, between November and January the syllabus gets to strings and files, right after loops and functions. The test is usually a practical lab exam, five to nine weeks from today: a text to read, a program to write and run. This is the right moment to practise, because a practical test is not prepared by rereading: it is prepared by writing code with your hands on the keyboard.

Here you will find the rules for strings, the methods you really need, reading and writing files and three exercises with complete code and output. I ran every line: you can copy and try. If Python is still new to you, start from learning Python from scratch.

Strings are immutable sequences

A string is a sequence of characters, and each character has an index that starts at 0. Negative indices count from the end: -1 is the last character.

phrase = "Python is great"
print(phrase[0])      # P
print(phrase[-1])     # t
print(phrase[2:5])    # tho
print(phrase[::-1])   # taerg si nohtyP

Slicing s[a:b] takes from character a up to b excluded. The third value is the step: [::-1] runs through the string backwards and reverses it.

Strings are immutable: you cannot write phrase[0] = "J", Python gives TypeError. To change something you build a new string and assign it to a variable.

The methods to know

MethodWhat it doesExample
upper() / lower()upper case / lower case"hello".upper() → 'HELLO'
strip()removes spaces and \n at the ends" hello ".strip() → 'hello'
split()splits into a list"a b c".split() → ['a', 'b', 'c']
replace(a, b)replaces"banana".replace("a", "o") → 'bonono'
find(x)index of the first occurrence, -1 if absent"Python is great".find("is") → 7
separator.join(list)joins a list"-".join(["a","b","c"]) → 'a-b-c'

Every method returns a new string and does not modify the original one: if you write only s.upper() without assigning the result, nothing happens to your s. It is one of the most frequent mistakes in the lab.

To visit the characters you use a for loop:

def vowels(s):
    n = 0
    for c in s.lower():
        if c in "aeiou":
            n += 1
    return n

print(vowels("Programming"))   # 3

The vowels in “Programming” are o, a, i: three. The loop for c in s gives each character, with no need for indices, and the form c in "aeiou" checks whether the character appears in the string.

The Caesar cipher with ord and chr

ord(c) returns the numeric code of a character, chr(n) does the opposite: ord("a") is 97, chr(100) is 'd'. With these two you shift a letter by k places:

def caesar(text, k):
    result = ""
    for c in text:
        if "a" <= c <= "z":
            new = (ord(c) - ord("a") + k) % 26 + ord("a")
            result += chr(new)
        else:
            result += c
    return result

print(caesar("hello world", 3))   # khoor zruog

The key step is % 26: it shifts the letter without leaving the alphabet, and so x with k = 3 becomes a. Spaces and symbols are not letters and pass through unchanged. The cipher is also explained from the maths side in cryptography: Caesar, Vigenère, RSA.

Working with files

The correct way to open a file is with, which closes it by itself at the end of the block:

with open("grades.txt") as f:
    for line in f:
        print(line.strip())

The loop for line in f reads one line at a time, and every line carries with it the newline character \n: that is why you almost always need strip(). There are three opening modes:

  • "r" (default): reading. If the file does not exist, error.
  • "w": writing. If the file exists, it empties it immediately.
  • "a": appending at the end, without touching the contents.

If you need all the contents at once, f.read() returns a single string, while f.readlines() returns a list with one string per line, \n included. For small files both are fine; the line-by-line loop remains the tidiest choice and also works with large files.

To write you use f.write(text), and here too the \n has to be added by hand: f.write("Anna\n"). A file that does not exist is handled like this:

try:
    with open("grades.txt") as f:
        content = f.read()
except FileNotFoundError:
    print("File not found")

Exercise 1: palindrome and anagram

Write a function that says whether a sentence is a palindrome (it reads the same from right to left, ignoring spaces and capitals) and one that says whether two words are anagrams.

def is_palindrome(s):
    clean = s.replace(" ", "").lower()
    return clean == clean[::-1]

def is_anagram(a, b):
    return sorted(a.lower()) == sorted(b.lower())

print(is_palindrome("Anna"))                         # True
print(is_palindrome("Was it a car or a cat I saw"))  # True
print(is_palindrome("hello"))                        # False
print(is_anagram("Listen", "Silent"))                # True
print(is_anagram("house", "horse"))                  # False

For the palindrome I remove the spaces with replace, put everything in lower case and compare the string with its reversed version. For the anagram sorted turns each word into a list of sorted letters: two words are anagrams if, once sorted, they give the same list. “listen” and “silent” both give ['e','i','l','n','s','t']; “house” and “horse” do not, because one has a u where the other has an r.

Exercise 2: words and longest line of a file

The file words.txt contains three lines. Count the total words and find the longest line.

the cat sleeps
on the sofa today
it rains
n_words = 0
longest = ""

with open("words.txt") as f:
    for line in f:
        line = line.strip()
        n_words += len(line.split())
        if len(line) > len(longest):
            longest = line

print("Words:", n_words)
print("Longest line:", longest, "(", len(longest), "characters )")

Output:

Words: 9
Longest line: on the sofa today ( 17 characters )

The lines have 3, 4 and 2 words: 9 in all. “on the sofa today” has 2 + 1 + 3 + 1 + 4 + 1 + 5 = 17 characters, counting the spaces, and is longer than “the cat sleeps”, which has 14. The strip() comes before the count: without it, every line would have one extra character for the \n, and the result would be wrong, 15 instead of 14 for the first line.

Exercise 3: average of grades read from a file

The file grades.txt contains, for each student, the name and three grades separated by commas. Print the average of each and the average of all the grades.

Anna,7,8,6
Marco,5,6,7
Sara,9,8,10
total = 0
count = 0

try:
    with open("grades.txt") as f:
        for line in f:
            fields = line.strip().split(",")
            name = fields[0]
            grades = [int(x) for x in fields[1:]]
            avg = sum(grades) / len(grades)
            print(name, round(avg, 2))
            total += sum(grades)
            count += len(grades)
    print("Overall average:", round(total / count, 2))
except FileNotFoundError:
    print("File not found")

Output:

Anna 7.0
Marco 6.0
Sara 9.0
Overall average: 7.33

Each line is split on the commas: fields[0] is the name, fields[1:] are the grades, still strings, so they have to be converted with int. Anna: 21 / 3 = 7; Marco: 18 / 3 = 6; Sara: 27 / 3 = 9. The overall average is 66 / 9 = 7.33. The sums accumulate outside the loop, otherwise the value resets on every line.

The mistakes that cost marks in the lab

  • Forgetting the \n: when reading, not removing it with strip() distorts lengths and comparisons; when writing, without \n all the lines end up stuck together.
  • Modifying a string in place: s[0] = "x" does not work, and s.upper() without assignment does not change s.
  • Opening in "w" a file you wanted to read or complete: it empties it before you even write. To append you use "a".
  • Index out of range: s[len(s)] does not exist, the last valid index is len(s) - 1. With for c in s the problem does not arise.
  • Empty lines at the end of the file: "".split(",") gives [''] and int('') gives ValueError. If the file has an empty last line, check first if line.strip():.

For nested loops and the use of indices, which are taken as known here, there is loops and arrays in Year 3 computer science; the functions used above are in passing parameters, and for those who want the next step, recursion, factorial and Fibonacci. The page subjects: computer science explains how I work.

Simulator and next step

For computer science there is no dedicated simulator: in the practice tests for the liceo you will find maths and physics tests, so it is not the right category for this test. The best way to prepare is to redo the three exercises without looking, changing the data: another file, another sentence, an extra column.

If the lab test frightens you, write to me through the contact page: the free 30-minute introductory call is where we look together at where you get stuck.

Frequently asked questions

Why can't I change a single character of a string in Python?

Because strings are immutable: to change them you build a new string, for example with replace, with slicing or by concatenating characters in a loop.

What is the difference between opening a file in 'w' mode and 'a' mode?

The 'w' mode creates the file or wipes its contents, the 'a' mode appends at the end without touching what is already there. Using the wrong mode is a classic way to lose data.

Why do you use with when opening a file?

Because the with block closes the file by itself at the end, even if there is an error in the middle, so you don't risk leaving it open or losing data not yet written.

How do you handle a file that does not exist?

You wrap the opening in a try block and catch FileNotFoundError in the except block, printing a clear message instead of the error that stops the program.

Keep reading

Want to put what you read into practice?

Let's talk for 30 minutes, free: we look together at where you are stuck and I tell you honestly how I can help.

Or message me on WhatsApp.