The file.tell() method returns an integer representing the current position of the file stream pointer as a byte offset from the beginning of the file.
Bookmarking and Resumption: Pairing tell() with file.seek() enables robust stream bookmarking. You can record the current cursor position, navigate to another section of the file, and restore the exact read position later.
The standard CPython signature for file.tell() is:
file_object.tell()
| Component | Type | Parameters? | Behavior & Description |
|---|---|---|---|
| Arguments | None | None | tell() takes no arguments. Calling tell(x) raises TypeError. |
| Return Value | int |
— | The current file position. In binary mode, represents the exact byte index from the start of the file. In text mode, returns an opaque cookie number encoding decoder state and offset. |
| Exceptions | OSError |
— | Raised with [Errno 29] Illegal seek if called on unseekable streams such as pipes (sys.stdin) or network sockets. |
Every I/O method advances the internal file pointer. Inspecting tell() reveals how much content has been consumed or produced:
with open("sample.txt", "r", encoding="utf-8") as fob:
print("Initial position at file start:", fob.tell()) # 0
first_line = fob.readline()
print("Position after first line:", fob.tell())
remaining = fob.read()
print("Position at end of file (EOF):", fob.tell())
A common misunderstanding is expecting tell() in text mode to represent the count of characters read. In reality, Python's text decoder operates on underlying binary bytes:
'rb'): tell() returns a strict, intuitive linear byte index (0, 10, 1024...).'r'): tell() returns an opaque integer (cookie). This number encapsulates decoder buffering state, multi-byte UTF-8 code point positions, and newline normalization (e.g. converting Windows \r\n to \n).Never perform arithmetic on text mode tell() values. Do not calculate tell() + 10 or subtract numbers. Only treat text mode return values as opaque bookmarks to pass directly back to file.seek(saved_position).
Save a bookmark before inspecting subsequent data so you can seamlessly rewind back to your checkpoint:
with open("config.ini", "r", encoding="utf-8") as stream:
header = stream.readline()
# Record checkpoint position
checkpoint = stream.tell()
# Peek forward to inspect next line
peek_line = stream.readline()
print("Peeking at next line:", peek_line.strip())
# Restore cursor exactly back to the checkpoint
stream.seek(checkpoint)
print("Restored position:", stream.tell())
By comparing the current cursor offset from tell() against the known total file size from os.path.getsize(), you can display accurate progress metrics during long-running batch jobs:
import os
def process_file_with_progress(filepath: str) -> None:
total_size = os.path.getsize(filepath)
with open(filepath, "rb") as f:
while chunk := f.read(65536):
current_offset = f.tell()
percent = (current_offset / total_size) * 100
print(f"\rProcessing: {percent:.1f}% ({current_offset:,} / {total_size:,} bytes)", end="")
print("\nComplete.")
Determine total file size on open streams without querying filesystem paths or OS stat metadata:
import os
def get_stream_size(stream) -> int:
# Save current position
current_pos = stream.tell()
# Jump to the end
stream.seek(0, os.SEEK_END)
size = stream.tell()
# Restore original position
stream.seek(current_pos)
return size
with open("database.bin", "rb") as f:
print("Stream length in bytes:", get_stream_size(f))
Store tell() offsets in state files or databases so batch pipelines can resume processing immediately from the last saved byte offset:
def read_from_checkpoint(filepath: str, last_saved_offset: int):
with open(filepath, "rb") as stream:
stream.seek(last_saved_offset)
while chunk := stream.read(4096):
# Process chunk...
yield chunk, stream.tell()
When iterating over a file with for line in stream:, Python employs internal readahead buffers. In older Python versions, calling tell() during line iteration was disabled. While modern Python supports it, the returned offset may point ahead of your current line due to buffering. Prefer stream.readline() if exact per-line offsets are required.
Streams like pipes, FIFOs, and network sockets cannot report offsets. Calling tell() on standard input (sys.stdin) raises OSError: [Errno 29] Illegal seek. Verify stream capability using stream.seekable() first.
Execute this self-contained test harness verifying offset tracking, bookmark restoration, and dynamic size calculation:
import tempfile
import os
class TellVerifier:
@staticmethod
def calculate_stream_size(path: str) -> int:
with open(path, "rb") as stream:
stream.seek(0, os.SEEK_END)
return stream.tell()
# Verification unit assertions
with tempfile.TemporaryDirectory() as td:
v = TellVerifier()
test_file = os.path.join(td, "verify.txt")
sample_data = "Header: System Log\nLine 1: OK\nLine 2: DONE\n"
with open(test_file, "w", encoding="utf-8") as f:
f.write(sample_data)
# Test 1: tell() starts at 0
with open(test_file, "r", encoding="utf-8") as f:
assert f.tell() == 0
f.readline()
mark = f.tell()
assert mark > 0
# Test 2: Bookmarking and rewinding
line2 = f.readline()
f.seek(mark)
assert f.readline() == line2
# Test 3: Dynamic stream size equals OS file size
expected_size = os.path.getsize(test_file)
assert v.calculate_stream_size(test_file) == expected_size
print("All file.tell() unit assertions passed successfully!")
Open in Google Colab View on GitHub
Run all file tell exercises interactively, test stream bookmarking with seek(), calculate stream sizes dynamically, and monitor file reading progress in Google Colab.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.