Python seek() and tell() Explained — File Cursor Control for Beginners
When you open a file in Python, there is an invisible cursor (also called a file pointer) that keeps track of where you are inside the file. Every time you read or write, this cursor moves automatically. seek() and tell() give you full control over this cursor.
🔹 What is a File Cursor?
Think of a file like a long tape. When you open it, the cursor starts at position 0 (the very beginning). Every character you read moves the cursor forward by 1. If you read 5 characters, the cursor is now at position 5.
File content: H e l l o W o r l d Position: 0 1 2 3 4 5 6 7 8 9 10
seek() → Move the cursor to any position manually
tell() → Ask "where is the cursor right now?"
🔹 Step 0: Create a Sample File First
We will use this file in all examples below. Run this once:
# Create sample file
with open("demo.txt", "w") as f:
f.write("Hello World")
File content: Hello World (11 characters, positions 0–10)
🔹 1. tell() — Know the Current Cursor Position
f = open("demo.txt", "r")
print(f.tell()) # Before reading anything
f.read(5) # Read 5 characters: "Hello"
print(f.tell()) # After reading 5 characters
f.read(1) # Read 1 more character: " "
print(f.tell()) # After reading 6 characters
f.close()
✅ Output:
0 5 6
✅ Explanation:
- At start → cursor is at position 0
- After reading "Hello" (5 chars) → cursor is at position 5
- After reading " " (1 char) → cursor is at position 6
tell()simply returns an integer — the byte offset from the start
💡 Real Scenario:
You are processing a large log file. Before reading a block of data, you save the current position using tell(). If something goes wrong, you can jump back to exactly that position.
🔹 2. seek() — Move the Cursor to Any Position
📌 Syntax:
file.seek(offset, whence)
- offset → How many bytes to move
- whence → From where to count (explained below)
🔹 3. Understanding offset and whence
These two terms confuse most beginners. Let's break them down clearly.
📌 offset
This is the number of bytes (characters) to move. Think of it as the distance to travel.
seek(0)→ Go to byte 0 (start of file)seek(5)→ Go to byte 5seek(0, 2)→ Go to end of file (using whence=2)
📌 whence (the reference point)
whence tells Python from where to start counting the offset. There are 3 values:
| whence value | Constant Name | Means "start from..." |
|---|---|---|
0 (default) |
os.SEEK_SET |
Beginning of the file |
1 |
os.SEEK_CUR |
Current cursor position |
2 |
os.SEEK_END |
End of the file |
⚠️ Note: whence=1 and whence=2 only work in binary mode ("rb", "wb"). In text mode ("r"), only whence=0 is fully supported.
🔹 4. seek() with whence=0 — From Start of File (Default)
# File content: "Hello World"
f = open("demo.txt", "r")
f.read(5) # Read "Hello", cursor now at 5
print(f.tell()) # 5
f.seek(0) # Move cursor back to start
print(f.tell()) # 0
print(f.read(5)) # Read again from start → "Hello"
f.seek(6) # Jump to position 6
print(f.read()) # Read from position 6 to end
f.close()
✅ Output:
5 0 Hello World
✅ Explanation:
seek(0)→ Rewind to the very beginning (like pressing rewind on a tape)seek(6)→ Jump directly to byte 6 (the "W" in "World")- Reading after seek starts from that position
🔹 5. seek() with whence=1 — From Current Position
This works only in binary mode. Offset can be positive (forward) or negative (backward).
# File content: "Hello World"
f = open("demo.txt", "rb") # binary mode
f.read(5) # Read "Hello", cursor at 5
print(f.tell()) # 5
f.seek(2, 1) # Move 2 bytes FORWARD from current (5+2=7)
print(f.tell()) # 7
print(f.read(4)) # Read from 7 → b'orld'
f.seek(-3, 1) # Move 3 bytes BACKWARD from current position
print(f.tell()) # Depends on above read
f.close()
✅ Output:
5 7 b'orld' 8
✅ Explanation:
- After reading "Hello", cursor is at 5
seek(2, 1)→ move 2 bytes forward from 5 → cursor goes to 7seek(-3, 1)→ move 3 bytes backward from current → cursor goes back- Negative offset + whence=1 lets you "step back" from where you are
🔹 6. seek() with whence=2 — From End of File
This also works only in binary mode. Offset is usually negative (to go backwards from the end).
# File content: "Hello World" (11 chars, positions 0-10)
f = open("demo.txt", "rb")
f.seek(0, 2) # Go to the very END of file
print(f.tell()) # 11 (total file size)
f.seek(-5, 2) # Go 5 bytes BEFORE the end
print(f.tell()) # 6
print(f.read()) # Read last 5 bytes
f.close()
✅ Output:
11 6 b'World'
✅ Explanation:
seek(0, 2)→ jump to end.tell()returns file size (11)seek(-5, 2)→ go 5 bytes before end → position 6- Reading from 6 gives "World" (last 5 characters)
- Great for reading the last N bytes of large files efficiently
🔹 7. How Read & Write Change Cursor Position
This is something many beginners miss. Let's see exactly how the cursor moves during reads and writes.
📌 Reading moves cursor forward
f = open("demo.txt", "r")
print(f.tell()) # 0 → start
f.read(1)
print(f.tell()) # 1 → moved by 1
f.read(3)
print(f.tell()) # 4 → moved by 3 more
f.read()
print(f.tell()) # 11 → read all remaining, now at end
f.close()
✅ Output:
0 1 4 11
📌 Writing moves cursor forward too
f = open("demo.txt", "w") # opens and CLEARS the file
print(f.tell()) # 0 → fresh file, cursor at start
f.write("Hello")
print(f.tell()) # 5 → cursor moved by 5
f.write(" World")
print(f.tell()) # 11 → cursor at end
f.close()
✅ Output:
0 5 11
📌 Appending mode ("a") — cursor starts at end
# File already contains "Hello World"
f = open("demo.txt", "a")
print(f.tell()) # 11 → cursor starts at END in append mode
f.write("!!!")
print(f.tell()) # 14
f.close()
✅ Output:
11 14
File now contains: Hello World!!!
🔹 8. Practical Example — Read a File in Reverse Order
Using seek() we can read the last part of a file first — useful for log files.
# First write some lines
with open("log.txt", "w") as f:
f.write("Line 1: App started\n")
f.write("Line 2: User logged in\n")
f.write("Line 3: Error occurred\n")
# Now read only the last 23 characters
with open("log.txt", "rb") as f:
f.seek(0, 2) # Go to end
size = f.tell() # Get total size
print("File size:", size)
f.seek(-23, 2) # Go 23 bytes before end
print(f.read().decode()) # Read and decode bytes to string
✅ Output:
File size: 64 Line 3: Error occurred
💡 Real Scenario:
Server log files can be GBs large. Using seek(-N, 2) you can instantly jump to the last N bytes and read recent errors — without loading the entire file into memory.
🔹 9. Practical Example — Save & Restore Position (Bookmark)
f = open("demo.txt", "r") # File: "Hello World"
f.read(5) # Read "Hello"
# Save current position like a bookmark
bookmark = f.tell()
print("Bookmark saved at:", bookmark) # 5
f.read(3) # Read " Wo" → cursor at 8
# ... do some other work ...
# Jump back to bookmark
f.seek(bookmark)
print("After restoring bookmark:", f.tell()) # 5
print(f.read()) # " World" from position 5
f.close()
✅ Output:
Bookmark saved at: 5 After restoring bookmark: 5 World
💡 Real Scenario:
Resume file downloads, process large CSV files in chunks, or re-read a specific section of data without reopening the file.
🔹 10. seek() + tell() Together — Full Demo
with open("demo.txt", "rb") as f:
print("Start position :", f.tell()) # 0
data = f.read(5)
print("Read:", data) # b'Hello'
print("After read :", f.tell()) # 5
f.seek(0) # Back to start
print("After seek(0) :", f.tell()) # 0
f.seek(6) # Jump to position 6
print("After seek(6) :", f.tell()) # 6
print("Read:", f.read()) # b'World'
f.seek(0, 2) # Jump to end
print("End of file :", f.tell()) # 11
✅ Output:
Start position : 0 Read: b'Hello' After read : 5 After seek(0) : 0 After read : 5 After seek(6) : 6 Read: b'World' End of file : 11
🔥 Quick Reference Table
| Code | What it does | Mode needed |
|---|---|---|
f.tell() |
Returns current cursor position (int) | Any |
f.seek(0) |
Go to start of file | Any |
f.seek(N) |
Go to byte N from start | Any |
f.seek(N, 0) |
Same as above (explicit whence=0) | Any |
f.seek(N, 1) |
Move N bytes from current position | Binary only |
f.seek(-N, 1) |
Move N bytes BACKWARD from current | Binary only |
f.seek(0, 2) |
Go to end of file | Binary only |
f.seek(-N, 2) |
Go N bytes before end of file | Binary only |
🚀 Pro Tips
- Always use
f.seek(0)to re-read a file without closing and reopening it - Use
f.seek(0, 2)+f.tell()to get the file size in bytes - Use
tell()before reading a record so you canseek()back to it later - For large files, always use binary mode with seek for full control
- Use
with open(...)so the file closes automatically — cursor is destroyed safely
📌 Get File Size Using seek + tell:
with open("demo.txt", "rb") as f:
f.seek(0, 2) # Jump to end
size = f.tell() # End position = file size
print("File size:", size, "bytes")
✅ Output:
File size: 11 bytes
❌ Common Mistakes
- Using whence=1 or 2 in text mode → raises
UnsupportedOperationerror. Always switch to"rb" - Forgetting cursor position after read → reading returns empty string because cursor is at end. Fix:
f.seek(0) - Using positive offset with whence=2 → seeking beyond end of file causes unexpected behavior
- Assuming seek() returns data → it only moves the cursor. You still need
f.read()to get data - Using seek() on files opened in write-only mode ("w") without reading → confusing results
📌 Error Example:
f = open("demo.txt", "r") # text mode
f.seek(0, 2) # ❌ This raises error!
io.UnsupportedOperation: can't do nonzero end-relative seeks
📌 Fix:
f = open("demo.txt", "rb") # ✅ binary mode
f.seek(0, 2) # Works perfectly
🔚 Conclusion
Python's seek() and tell() give you precise control over the file cursor — letting you jump to any position, read specific sections, and process files efficiently without loading everything into memory.
- tell() → tells you where the cursor is right now
- seek(offset) → moves cursor from the beginning
- seek(offset, 1) → moves cursor relative to current position
- seek(offset, 2) → moves cursor relative to end of file
Mastering these functions is essential for working with large files, binary data, log files, and building efficient file-based applications ⚡