Why This Matters: The Mess Notebook
In a college hostel in Pune, the mess manager keeps a notebook. Every morning he writes who came for breakfast. At the end of the month, he reads the notebook to count plates and plan rations. The notebook is useful because it remembers. Close it today, open it next week, and the writing is still there.
Python variables do not remember anything. When your program ends, every list and dictionary disappears. To keep data for later, a program must write it to a file, and read it back the next time it runs.
In this lesson you will open files in different modes, write and read text, loop over lines, handle Hindi text with the right encoding, keep a log, count words, use pathlib, and deal with a file that does not exist.
About the playground: every Try it yourself run starts in a fresh, empty folder, so each example below first creates the file it reads. On your own computer, files stay in the folder where you run your program, and you can open them in VS Code.
open() and the with Block
The built-in open() function connects your program to a file. It takes the file name and a mode that says what you want to do. The safest way to use it is inside a with block:
# Step 1: write. "w" creates the file (or empties it if it already exists)
with open("attendance.txt", "w", encoding="utf-8") as file:
file.write("Rahul\n")
file.write("Sneha\n")
file.write("Arif\n")
# Step 2: read. "r" opens the file for reading
with open("attendance.txt", "r", encoding="utf-8") as file:
text = file.read()
print(text)
# Rahul
# Sneha
# Arif
The with block opens the file, gives it to you as file, and closes it automatically when the indented block ends, even if an error happens. A closed file is saved properly and released for other programs. Without with, you must remember to call file.close() yourself, and it is easy to forget.
Notice the \n at the end of each name. write() writes exactly the text you give it and nothing more. \n is the new line character, which ends the line.
The Three Main Modes: r, w and a
| Mode | Meaning | If the file does not exist | If the file exists |
|---|---|---|---|
"r" | Read (the default) | FileNotFoundError | Reads from the start |
"w" | Write | Creates it | Erases everything, then writes |
"a" | Append | Creates it | Keeps the old text and adds at the end |
The difference between "w" and "a" is the most important thing to remember in this lesson. Watch what happens to Monday's menu:
# "w" writes a fresh file
with open("menu.txt", "w", encoding="utf-8") as file:
file.write("Monday: Poha\n")
# "w" again: the old content is ERASED first
with open("menu.txt", "w", encoding="utf-8") as file:
file.write("Tuesday: Upma\n")
# "a" appends: new text goes at the end, old text stays
with open("menu.txt", "a", encoding="utf-8") as file:
file.write("Wednesday: Idli Sambar\n")
file.write("Thursday: Aloo Paratha\n")
with open("menu.txt", encoding="utf-8") as file: # "r" is the default mode
print(file.read())
# Tuesday: Upma
# Wednesday: Idli Sambar
# Thursday: Aloo Paratha
# (Monday is gone: the second "w" erased it)
Writing Many Lines and Reading in Different Ways
writelines() writes every string from a list. It does not add new lines for you, so each string needs its own \n. For reading, you have three methods:
read()gives the whole file as one string.readline()gives the next single line each time you call it.readlines()gives a list with one string per line.
students = ["Rahul\n", "Sneha\n", "Arif\n", "Divya\n"]
with open("attendance.txt", "w", encoding="utf-8") as file:
file.writelines(students) # writes every string in the list
# readline(): one line at a time
with open("attendance.txt", encoding="utf-8") as file:
first = file.readline()
second = file.readline()
print(repr(first), repr(second)) # 'Rahul\n' 'Sneha\n'
# readlines(): all lines as a list
with open("attendance.txt", encoding="utf-8") as file:
lines = file.readlines()
print(lines) # ['Rahul\n', 'Sneha\n', 'Arif\n', 'Divya\n']
print(len(lines), "students") # 4 students
repr() shows a string with its hidden characters, so you can see the \n that is really there at the end of each line.
Looping Over Lines and Using strip()
The most common way to read a file is a for loop directly over the file. It gives you one line per loop and never loads the whole file into memory, so it works even for very big files. Each line still ends with \n, so call strip() to remove it (and any extra spaces) before you use the line.
# Create the file first (each run starts with an empty folder)
with open("breakfast.txt", "w", encoding="utf-8") as file:
file.write("Rahul,present\n")
file.write("Sneha,absent\n")
file.write("Arif,present\n")
file.write("Divya,present\n")
present = 0
with open("breakfast.txt", encoding="utf-8") as file:
for line in file: # one line per loop, memory-friendly
line = line.strip() # remove the "\n" at the end
name, status = line.split(",")
print(f"{name:<6} {status}")
if status == "present":
present += 1
print("Breakfast plates needed:", present) # Breakfast plates needed: 3
Here each line holds two values separated by a comma, and split(",") breaks it into a name and a status. This simple idea is the start of the CSV format, which you will meet in the JSON and CSV lesson.
encoding="utf-8": Hindi and Other Languages
A computer stores text as numbers. An encoding is the rule that turns characters into those numbers. UTF-8 is the modern standard. It can store every script, including Hindi, Tamil, Bengali and emoji. The hostel's Hindi menu works perfectly with it:
hindi_menu = [
"सोमवार: पोहा और चाय\n",
"मंगलवार: उपमा और केला\n",
"बुधवार: इडली सांभर\n",
]
with open("menu_hindi.txt", "w", encoding="utf-8") as file:
file.writelines(hindi_menu)
with open("menu_hindi.txt", encoding="utf-8") as file:
for line in file:
print(line.strip())
# सोमवार: पोहा और चाय
# मंगलवार: उपमा और केला
# बुधवार: इडली सांभर
Make it a habit: always write encoding="utf-8" when you open a text file. Without it, Python uses your computer's default, which on some Windows computers is not UTF-8. A file with Hindi, Tamil or even the Rs symbol can then fail to save or show strange characters.
Counting Words in a File
Once the text is in a string, all your string skills work. split() with no argument splits on any spaces and new lines, so len(text.split()) counts the words. A dictionary can count how often each word appears.
notice = """Dear students, the mess will serve a special dinner on Saturday.
Please mark your attendance by Friday evening.
Students who do not mark attendance will not get a coupon."""
with open("notice.txt", "w", encoding="utf-8") as file:
file.write(notice)
with open("notice.txt", encoding="utf-8") as file:
text = file.read()
lines = text.splitlines()
words = text.split() # split() with no argument splits on any spaces or new lines
print("Lines:", len(lines)) # Lines: 3
print("Words:", len(words)) # Words: 29
# How many times does each word appear? (lower case, without full stops and commas)
counts = {}
for word in words:
word = word.lower().strip(".,")
counts[word] = counts.get(word, 0) + 1
print("'attendance' appears", counts["attendance"], "times") # 2 times
pathlib: A Modern Way to Work With Files
The pathlib module from the standard library gives you a Path object for a file name. It has useful methods: exists() checks whether the file is there, and read_text() and write_text() read or write a whole file in one line, opening and closing it for you.
from pathlib import Path
notes = Path("mess_notes.txt")
print(notes.exists()) # False: nothing written yet
notes.write_text("Gas cylinder changed\nNew cook joins on Monday\n", encoding="utf-8")
print(notes.exists()) # True
text = notes.read_text(encoding="utf-8")
print(text.splitlines()) # ['Gas cylinder changed', 'New cook joins on Monday']
print(notes.name) # mess_notes.txt
print(notes.suffix) # .txt
print(notes.stem) # mess_notes
Use read_text() and write_text() for small files. For adding lines one by one, or reading a big file line by line, open() with with is still the right tool, and it accepts a Path too.
Real-Life Example: The Mess Meal Log
Now the manager's notebook becomes a program. record_meal() appends one line for each meal, so the log grows day by day and nothing is lost. daily_summary() reads the log and adds up the plates for each day. The first summary runs before any meal is recorded, to show that the program handles a missing file politely.
from pathlib import Path
LOG = Path("mess_log.txt")
def record_meal(day, meal, students):
"""Add one line to the log. 'a' keeps all the earlier lines."""
with open(LOG, "a", encoding="utf-8") as file:
file.write(f"{day},{meal},{students}\n")
def daily_summary():
if not LOG.exists():
print("No meals recorded yet.")
return
totals = {}
with open(LOG, encoding="utf-8") as file:
for line in file:
line = line.strip()
if not line: # skip empty lines
continue
day, meal, students = line.split(",")
totals[day] = totals.get(day, 0) + int(students)
for day, plates in totals.items():
print(f"{day:<10} {plates:>4} plates")
print(f"{'Total':<10} {sum(totals.values()):>4} plates")
daily_summary() # nothing yet
record_meal("Monday", "Breakfast", 112)
record_meal("Monday", "Lunch", 130)
record_meal("Monday", "Dinner", 125)
record_meal("Tuesday", "Breakfast", 108)
record_meal("Tuesday", "Lunch", 134)
daily_summary()
print()
print("Raw file:")
print(LOG.read_text(encoding="utf-8"))
Try these changes:
- Record Tuesday's dinner and run it again.
- Change the summary to show the total for each meal (Breakfast, Lunch, Dinner) instead of each day.
- Change
"a"to"w"inrecord_meal()and see what is left in the log. Then change it back!
Common Mistakes
- Reading a file that does not exist. Mode
"r"never creates a file, so Python stops with an error:
with open("feedback.txt", encoding="utf-8") as file: # ERROR: FileNotFoundError
print(file.read())
Fix: check first with Path.exists(), as below. In the Error Handling lesson you will also learn to catch this error with try and except.
from pathlib import Path
feedback = Path("feedback.txt")
if feedback.exists():
print(feedback.read_text(encoding="utf-8"))
else:
print("No feedback yet. Creating an empty file.")
feedback.write_text("", encoding="utf-8")
- Using "w" when you meant "a". One wrong letter erases a month of records. Fix: use
"a"to add to a log, and"w"only when you want to replace the whole file. - Forgetting
\n.write("Rahul")followed bywrite("Sneha")givesRahulSnehaon one line. Fix: end each line with\n. - Forgetting
strip()."present\n" == "present"isFalse, so your counts come out as zero. Fix: strip every line you read. - Leaving out the encoding. The code may work on your laptop and fail on a friend's. Fix: always pass
encoding="utf-8".
Practice Task: Mess Feedback File
The hostel collects one feedback line per day in the form day,rating,comment. Complete the TODO steps:
- Write the first three days with
writelines(). - Append two more days without losing the first three.
- Read the file line by line and count the good and bad days.
- Count all the words in the comments.
- Check with
Paththat the file exists.
from pathlib import Path
first_week = [
"Monday,good,The poha was hot and fresh\n",
"Tuesday,bad,Upma was too salty\n",
"Wednesday,good,Loved the idli\n",
]
# TODO 1: write first_week into "feedback.txt" with writelines()
# TODO 2: append two more lines: Thursday (good) and Friday (bad),
# each with a short comment
# TODO 3: read the file line by line, strip each line, and count
# how many days were "good" and how many were "bad"
# TODO 4: print the total number of words in all the comments
# TODO 5: use Path to print whether "feedback.txt" exists
Show one possible answer (try it yourself first!)
from pathlib import Path
first_week = [
"Monday,good,The poha was hot and fresh\n",
"Tuesday,bad,Upma was too salty\n",
"Wednesday,good,Loved the idli\n",
]
with open("feedback.txt", "w", encoding="utf-8") as file:
file.writelines(first_week)
with open("feedback.txt", "a", encoding="utf-8") as file:
file.write("Thursday,good,Parathas with curd\n")
file.write("Friday,bad,Dinner was served late\n")
good = 0
bad = 0
comment_words = 0
with open("feedback.txt", encoding="utf-8") as file:
for line in file:
day, rating, comment = line.strip().split(",")
if rating == "good":
good += 1
else:
bad += 1
comment_words += len(comment.split())
print("Good days:", good) # Good days: 3
print("Bad days:", bad) # Bad days: 2
print("Words in comments:", comment_words) # Words in comments: 20
print("File exists:", Path("feedback.txt").exists()) # File exists: True
Key Takeaways
- Files let your program remember data after it ends. Open them with
open()inside awithblock, which closes them for you. "r"reads,"w"erases and writes,"a"adds to the end. Choose carefully.write()andwritelines()do not add new lines: put\nyourself. Usestrip()when you read lines.- Loop directly over a file to read it line by line, and always pass
encoding="utf-8". pathlib.Pathgives youexists(),read_text()andwrite_text(). Reading a missing file raisesFileNotFoundError.