Free Study Material
When you call open(), Python doesn't just do something to the file — it hands you back a file object, a small object living in memory that represents your connection to that file on disk. This object remembers useful things about itself: its name, the mode it was opened in, and whether it's currently closed.
While a file is open, Python (and your operating system) treats it as "in use" — other programs may be blocked from touching it, and any data you've written might still be sitting in a temporary buffer rather than actually saved to disk. Calling .close() flushes that buffer and releases the file, which is why a file's .closed property flips from False to True the moment you call it.
f = open("test.txt", "w")
print(f.name)
print(f.mode)
print(f.closed)
f.close()
print(f.closed)
f.name prints test.txt and f.mode prints w — the file object remembers exactly what it was opened as. The two f.closed prints are the real point: before close() it reads False — the file is still open — and after close() it flips to True, in this same run.
Warning: Forgetting .close() doesn't crash your program — which is exactly what makes it dangerous. The bug is silent: your data might simply never make it to disk, and you won't find out until you reopen the file and the content you expected isn't there.
open() returns a file object with its own name, mode, and closed properties. Always close a file when you're done with it — an open file is not a guaranteed-saved file.
You don't edit the file directly on the disk. Instead, Python creates a File Object (also called a Handle or Pointer).
Think of the File Object as a Remote Control. The file is the TV. You use the remote (object) to change channels (read/write) on the TV (file).
We use the built-in function open(). It returns the file object.
"notes.txt")."r" for Read).This is the most forgotten step! When you are done, you MUST close the file.
f = open("sample.txt", "w") # 1. Open
f.write("Hello") # 2. Work
f.close() # 3. Close (Save)
# -----------------------------------------------------
# CHALLENGE: The File Lifecycle
# -----------------------------------------------------
# We will create a file object, check its properties, and close it.
#
# YOUR TASK:
# 1. Run the code. It prints 'False' because the file is OPEN.
# 2. Uncomment line 11 (f.close()).
# 3. Run again. It should print 'True' because the file is CLOSED.
# 1. Open a file named "test.txt" in Write mode ("w")
f = open("test.txt", "w")
print("File Name:", f.name)
print("File Mode:", f.mode)
# 2. Check if closed (Should be False)
print("Is file closed?", f.closed)
# TODO: Uncomment the line below to close it properly
# f.close()
# 3. Check again (Should be True)
print("Is file closed now?", f.closed)
The second argument to open() — "w", "r", "a", and a few others — tells Python exactly what you intend to do with the file, and Python behaves very differently depending on which one you pick.
| Mode | Name | What it does |
|---|---|---|
| r | Read | Opens an existing file to read from it. Errors if the file doesn't exist. |
| w | Write | Opens a file for writing. Erases the entire existing content first, or creates a brand-new empty file if none exists. |
| a | Append | Opens a file for writing, but keeps existing content and adds new data to the end. Creates the file if it doesn't exist. |
The mode you choose isn't just a technicality — picking "w" when you meant "a" is one of the most common real-world data-loss bugs in file handling, because "w" erases silently, with no warning.
f = open("history.txt", "w")
f.write("Chapter 1: The Beginning\n")
f.close()
# Using "w" again here wipes Chapter 1 completely!
f = open("history.txt", "w")
f.write("Chapter 2: The Middle\n")
f.close()
Run this and read the file back — only "Chapter 2" survives. Change the second "w" to "a" and both chapters appear, in order.
Warning: "w" mode erases a file's entire existing content the instant you open it, before you've even written anything new. If you only want to add to a file, use "a", never "w".
"r" reads, "w" overwrites (and can silently destroy existing data), "a" appends without touching what's already there. Choosing the wrong mode is a common source of accidental data loss.
Be very careful with Write Mode ('w'). It acts like recording over an old cassette tape.
When you open a file with 'w', Python assumes you want a Fresh Start. It immediately deletes everything currently in the file before you even write a single word.
If you want to keep the old data and just add new lines (like a Diary), use Append Mode ('a') instead.
# 1. We write Chapter 1
f = open("history.txt", "w")
f.write("Chapter 1: The Beginning\n")
f.close()
# 2. We try to add Chapter 2
# TODO: Change "w" to "a" below to fix the bug
f = open("history.txt", "w")
f.write("Chapter 2: The Middle\n")
f.close()
# 3. Read result
f = open("history.txt", "r")
print(f.read())
f.close()
Manually calling .close() has a real weakness: if something goes wrong between open() and close() — an error, an exception, a crash — your close() line never runs, and the file is left open and possibly unsaved.
Python's with statement solves this completely. It's called a context manager: it opens the file, runs your indented block of code, and then closes the file automatically — guaranteed, even if an error happens partway through. You never write .close() yourself again.
# Old, unsafe way -- close() might never run if an error happens above it
file = open("notes.txt", "w")
file.write("This is risky.")
file.close()
# Safe way -- closes automatically, no matter what
with open("notes.txt", "w") as file:
file.write("This is safe!")
print(file.closed)
That last line prints True — even though nothing called .close() by hand, the file is already closed by the time print() runs, because the with block above it already ended. That's the actual proof that with really did close the file automatically.
Tip: As soon as Python reaches the end of the indented block under "with", the file closes automatically. You don't need to, and shouldn't, call .close() inside it.
with open(...) as f: is the safe, modern way to work with files — it closes the file automatically the moment its indented block ends, even if an error occurs, removing an entire category of file-handling bugs.
We are humans. We forget things. In the previous method, if you forgot to type f.close(), or if your program crashed before reaching that line, the file stayed open. This causes data loss.
Python offers a smarter way: The with clause. It acts like an Automatic Door. As soon as your code finishes (or crashes), Python automatically closes the file for you. You never have to type close() again.
The syntax changes slightly. We put the open() command at the top, and all file operations must be indented inside the block.
with open("diary.txt", "w") as f: f.write("Dear Diary...") f.write("Python is cool.") # <-- The moment you un-indent here, the file CLOSES automatically. print("File is now closed.")
# OLD UNSAFE WAY (Refactor this!)
# file = open("notes.txt", "w")
# file.write("This is risky.")
# file.close()
# TODO: Write the SAFE version below using 'with open(...) as file:'
with open("notes.txt", "w") as file:
file.write("This is safe!")
# Check if it worked
print("File closed automatically?", file.closed)
Python gives you two main ways to write text into a file: .write() for a single string, and .writelines() for a whole list of strings at once. Both look similar, but there's one detail that trips up almost everyone the first time.
.write() only ever accepts a single string — not a number, not a list, just text. And .writelines(), despite its name, does not add a newline character between the items it writes — it just glues the list's strings together exactly as given.
colors = ["Red", "Green", "Blue"]
with open("colors.txt", "w") as f:
f.writelines(colors)
with open("colors.txt", "r") as f:
print(f.read())
The output reads RedGreenBlue — one unbroken line — because writelines() never inserted anything between the entries.
colors = ["Red\n", "Green\n", "Blue\n"]
with open("colors.txt", "w") as f:
f.writelines(colors)
with open("colors.txt", "r") as f:
print(f.read())
Add "\n" to each string and the same code prints Red, Green, and Blue on three separate lines — writelines() never adds line breaks for you, so if you want them, they have to already be part of the strings.
Note: writelines() never adds a newline between the items it writes — that's a real behavior difference from what you might expect, and it's on you to add "\n" yourself if you want each item on its own line.
.write() takes one string; .writelines() takes a list of strings but never inserts newlines automatically — include "\n" yourself wherever you want a line break.
Writing to a file isn't magic. You have two tools: a Pen (`write`) for single sentences, and a Photocopier (`writelines`) for lists of sentences.
But be careful! Unlike the print() command you know, file commands are "dumb." They do not add spaces or enter keys (newlines) unless you explicitly tell them to.
This is your standard tool. It takes exactly one string as an argument and puts it into the file.
Example 1: Basic Write
with open("story.txt", "w") as f:
f.write("Once upon a time.")
f.write("The End.")
Result in File: Once upon a time.The End.
Wait, why is it squashed? Because `write()` doesn't hit "Enter" for you. You must add `\n` yourself.
Example 2: Adding Newlines
with open("story.txt", "w") as f:
f.write("Once upon a time.\n") # \n moves cursor down
f.write("The End.")
What if you have a List of names? You can't use `write()` because it only accepts Strings.
You use `writelines()`. It takes a List (or any iterable) and dumps it into the file.
Example 3: Writing a List
fruits = ["Apple\n", "Banana\n", "Cherry\n"]
with open("basket.txt", "w") as f:
f.writelines(fruits)
# 1. A list without newlines
colors = ["Red", "Green", "Blue"]
# 2. Writing safely
with open("colors.txt", "w") as f:
f.writelines(colors)
# 3. Reading it back to check
with open("colors.txt", "r") as f:
print(f.read())
# TODO: Add "\n" to the strings in the list above to fix the output!
Every open file has an invisible cursor — CBSE calls it the file pointer — that tracks exactly where you are inside it, much like a bookmark in a book. Python gives you three ways to read from that position onward:
.read() — reads everything from the cursor to the end, as one big string..readline() — reads just the next single line..readlines() — reads everything as a list, one string per line.The detail that catches people out: after .read() finishes, the cursor is now sitting at the very end of the file. If you call .read() again immediately, there's nothing left to read from that position — you get back an empty string, not the file's content again.
with open("test.txt", "w") as f:
f.write("Hello from a text file!")
with open("test.txt", "r") as f:
data1 = f.read()
print("First Read:", data1)
data2 = f.read()
print("Second Read:", data2)
The first read prints Hello from a text file! in full. The second read comes back empty because the cursor never moved back to the start. Rewinding it with f.seek(0) before the second read fixes this — you'll meet seek() properly in the next topic.
Tip: A file's read cursor (file pointer) only ever moves forward as you read. If you need to read the same content twice, you must explicitly move the cursor back first.
.read(), .readline(), and .readlines() all read from the file's current cursor position — the file pointer — onward, and reading moves that cursor forward — so reading to the end and then reading again returns nothing, unless you reset the cursor first.
When you read a physical book, you use your finger to keep your place. Python does the same thing with a File Pointer (Cursor).
When you open a file, the cursor sits at the very beginning (Index 0). As you read, the cursor moves forward. If you read the whole file, the cursor ends up at the very bottom.
This is the "Vacuum". It sucks the entire file content into a Single String.
with open("story.txt", "r") as f:
content = f.read() # Reads EVERYTHING
print(content)
Variant: `read(n)`
You can tell the vacuum to stop after `n` characters.
f.read(5) reads only the first 5 letters.
This reads only one line at a time. It stops when it hits a newline character (`\n`).
It is memory efficient because it doesn't load the whole file at once.
with open("story.txt", "r") as f:
line1 = f.readline() # Reads line 1
line2 = f.readline() # Reads line 2
print(line1)
Notice the 's' at the end. This reads the entire file but chops it up line-by-line.
It returns a List of Strings.
with open("shopping.txt", "r") as f:
items = f.readlines()
print(items)
# Output: ['Milk\n', 'Eggs\n', 'Bread\n']
# Create a dummy file first
with open("test.txt", "w") as f:
f.write("Hello World!")
# Now let's try reading it twice
with open("test.txt", "r") as f:
data1 = f.read()
print("First Read:", data1)
# TODO: Uncomment the line below to reset the cursor!
# f.seek(0)
data2 = f.read()
print("Second Read:", data2)
You just saw that reading a file moves an invisible cursor forward, and that this can cause a second read to come back empty. seek() and tell() are the two methods that let you actually see and control that cursor directly.
f.tell() returns the cursor's current position, as a plain number (how many characters from the start).f.seek(n) jumps the cursor to position n — f.seek(0) is the one you'll use constantly, since it rewinds all the way back to the beginning.with open("secret.txt", "w") as f:
f.write("Top Secret Data")
with open("secret.txt", "r") as f:
print("Read 1:", f.read())
print("Current Position:", f.tell())
f.seek(0)
print("Read 2:", f.read())
"Top Secret Data" is 15 characters long, so after the first .read(), f.tell() prints 15 — the cursor is sitting right at the end. f.seek(0) sends it back to the start, so the second read prints the full text again instead of coming back empty.
Note: tell() only reports where the cursor is — it never changes it. seek() is the one that actually moves it.
tell() reports the cursor's current position; seek(n) moves it there — seek(0) rewinds to the start, which is exactly what's needed to re-read a file you've already read once.
Reading a file is like listening to a song. You can pause, check the timestamp, or scrub back to the beginning.
Python gives you two remote control buttons for this:
tell(): The Timer. It tells you exactly which byte (character) the cursor is currently touching.seek(): The Slider. It lets you jump the cursor to any position you want.
This returns an Integer representing the current byte position.
with open("abc.txt", "r") as f:
print(f.tell()) # Output: 0 (At Start)
f.read(5) # Read first 5 letters
print(f.tell()) # Output: 5 (Moved forward)
This moves the cursor to a specific byte index. The most common use is Rewinding to the start.
file_object.seek(offset)
f.seek(0): Go to the Start (Rewind).f.seek(10): Jump to the 10th byte.Note: In Text Mode ('r'), Python mostly allows seeking to the start (0) or a position returned by tell(). Advanced seeking is usually for Binary files.
# Create a dummy file
with open("secret.txt", "w") as f:
f.write("Top Secret Data")
# Read it
with open("secret.txt", "r") as f:
print("Read 1:", f.read())
# Cursor is now at the end!
print("Current Position:", f.tell())
# TODO: Add f.seek(0) here to rewind
print("Read 2:", f.read())
The three modes you already know — r, w, a — only let you either read OR write, never both in the same session. Adding a + unlocks both at once, but each one still keeps the personality of its base mode:
| Mode | Read | Write | Existing content | Cursor starts at |
|---|---|---|---|---|
| r+ | Yes | Yes | Kept | Start |
| w+ | Yes | Yes | Erased | Start |
| a+ | Yes | Yes | Kept | End |
r+ is the one you'll reach for most often — it's the only + mode that lets you both read the existing content and selectively overwrite parts of it in place, without wiping anything first.
with open("numbers.txt", "w") as f:
f.write("12345")
with open("numbers.txt", "r+") as f:
data = f.read(2)
f.write("A")
f.seek(0)
print("Final Content:", f.read())
Notice the write didn't insert — it overwrote whatever character was already sitting at the cursor's position.
Warning: w+ still erases the file's entire existing content the moment it's opened, exactly like plain w — the + only adds read access, it doesn't change that.
r+ reads and writes without erasing anything, starting from the beginning. w+ does the same but erases first. a+ reads and writes without erasing, but always starts writing at the very end.
Sometimes you need to read a document and fix a typo immediately without closing and reopening it. This is where the Plus Modes come in.
Adding a + to a mode simply adds the missing capability:
Best for: Modifying data without deleting everything.
Note: Writing in `r+` will overwrite characters one by one. It does not "insert" text; it replaces it.
Best for: Creating a new file, writing data, and immediately reading it back.
Best for: Adding to a log file and reviewing the history.
Tricky Part: Since the pointer starts at the end, if you try to `read()` immediately, you get nothing! You must use `seek(0)` to go back and read.
# Setup the file
with open("numbers.txt", "w") as f:
f.write("12345")
# Open in Read+Write Mode
with open("numbers.txt", "r+") as f:
data = f.read(2) # Reads "1", "2"
# Cursor is now at index 2 (before "3")
f.write("A") # Overwrites "3" with "A"
# Go back to start to see the whole file
f.seek(0)
print("Final Content:", f.read())
You've now covered the full toolkit for working with text files in Python:
- Opening and closing: open() returns a file object; with open(...) as f: closes it automatically. - Modes: "r" reads, "w" overwrites (erasing first), "a" appends without erasing. - Writing: .write() for one string, .writelines() for a list — neither adds newlines for you. - Reading: .read(), .readline(), .readlines() — all move the cursor forward as they go. - Cursor control: .tell() reports the cursor's position; .seek(n) moves it, with seek(0) as the most common rewind. - Combined modes: r+/w+/a+ unlock reading and writing together, each keeping its base mode's behavior around existing content.
The ten programs below put all of it together on one shared file, practice.txt — creating it, analyzing it, editing it in place, and cleaning it up. This is exactly the kind of real, practical file manipulation exam questions and real projects both expect you to write from memory.
Tip: A common technique appears again and again below: read the whole file into memory first, make your changes to that in-memory copy, then write the result back out. It's usually far simpler than trying to edit a file while it's still open.
Every text file operation in this chapter reduces to the same three steps: open it in the right mode, do your reading/writing/seeking, and let with close it safely. The ten practice programs are worked examples of that pattern applied to real tasks — counting, searching, replacing, and copying file content.
Everything we have learned about Text Files in one place.
| Mode | Action | Wipes Data? | Pointer |
|---|---|---|---|
| r | Read Only (Default) | ❌ No | Start |
| w | Write Only | ⚠️ YES | Start |
| a | Append Only | ❌ No | End |
| r+ | Read + Write | ❌ No | Start |
| w+ | Write + Read | ⚠️ YES | Start |
| Function | Description | Returns |
|---|---|---|
open(name, mode) | Opens the file. | File Object |
read(n) | Reads whole file (or n chars). | String |
readline() | Reads one line at a time. | String |
readlines() | Reads all lines into a list. | List |
write(str) | Writes a single string. | None |
writelines(list) | Writes a list of strings. | None |
seek(offset) | Moves cursor to new position. | None |
tell() | Returns current cursor position. | Integer |
# Program 1: Create a file with sample data
# We need this file for the other programs to work!
data = [
"Welcome to Kendriya Vidyalaya.\n",
"Computer Science is fun.\n",
"Are you ready for the exam?\n",
"Apples and Oranges are healthy.\n",
"The End."
]
with open("practice.txt", "w") as f:
f.writelines(data)
print("File 'practice.txt' created successfully!")
# Program 2: Count Characters, Words, and Lines
with open("practice.txt", "r") as f:
content = f.read()
char_count = len(content)
word_count = len(content.split())
# We use split() to break the string into words by spaces
# Reset cursor to count lines safely using readlines()
f.seek(0)
line_count = len(f.readlines())
print(f"Chars: {char_count}, Words: {word_count}, Lines: {line_count}")
# Program 3: Change "Vidyalaya" to "School"
# Strategy: Read All -> Replace in Memory -> Write All Back
with open("practice.txt", "r") as f:
data = f.read()
# Perform the swap
new_data = data.replace("Vidyalaya", "School")
# Overwrite the file with new data
with open("practice.txt", "w") as f:
f.write(new_data)
print("Replacement Complete. Check the file!")
# Program 4: Count words starting with A, E, I, O, U
count = 0
vowels = ['a', 'e', 'i', 'o', 'u']
with open("practice.txt", "r") as f:
content = f.read()
words = content.split()
for w in words:
# Check first letter (converted to lower case)
if w[0].lower() in vowels:
count += 1
print("Words starting with a vowel:", count)
# Program 5: Find lines that are Questions
count = 0
with open("practice.txt", "r") as f:
lines = f.readlines()
for line in lines:
# strip() removes the invisible '\n' at the end
if line.strip().endswith("?"):
count += 1
print("Number of questions found:", count)
# Program 6: Copy 'practice.txt' to 'backup.txt'
with open("practice.txt", "r") as r:
data = r.read()
with open("backup.txt", "w") as w:
w.write(data)
print("Backup created successfully.")
# Program 7: Add a new line without deleting old ones
# Mode 'a' is critical here!
with open("practice.txt", "a") as f:
f.write("\nProcess Completed Successfully.")
print("Log appended.")
# Program 8: Delete lines containing the word "Apple"
# Strategy: Read all lines -> Filter list -> Write back
with open("practice.txt", "r") as f:
lines = f.readlines()
with open("practice.txt", "w") as f:
for line in lines:
if "Apple" not in line:
f.write(line)
print("Lines with 'Apple' have been removed.")
# Program 9: Read the 5th character directly
with open("practice.txt", "r") as f:
f.seek(5) # Jump to index 5
char = f.read(1) # Read 1 character
pos = f.tell() # Get current location
print(f"Character at index 5 is '{char}'")
print(f"Cursor is now at {pos}")
# Program 10: Print only lines with more than 5 words
with open("practice.txt", "r") as f:
for line in f:
words = line.split()
if len(words) > 5:
print(f"Long Line: {line.strip()}")