Skip to content

Python Files I/O

This chapter covers the fundamental Input/Output (I/O) operations in Python, focusing on interacting with the console (standard input/output) and files.

The print() function is the primary way to display output to the standard output (usually the console). You can pass zero or more objects to print, separated by commas.

# Simple printing
print("Hello, Python!")
# Printing multiple items (separated by spaces by default)
name = "Alice"
age = 30
print("Name:", name, "Age:", age)
# Using f-strings for formatted output (recommended)
print(f"Name: {name}, Age: {age}")
# Controlling separator and end characters
print("Item 1", "Item 2", sep=" | ") # Custom separator
print("First line", end=" - ") # Custom end character (default is '\n')
print("Second line")

This produces the following result:

Hello, Python!
Name: Alice Age: 30
Name: Alice, Age: 30
Item 1 | Item 2
First line - Second line

Python provides the built-in input() function to read a line of text from standard input (usually the keyboard).

The input([prompt]) function displays the optional prompt string, waits for the user to type input and press Enter, and then returns the entered text as a string.

#!/usr/bin/env python3
user_name = input("Enter your name: ")
print(f"Hello, {user_name}!")
# Input is always a string, convert if necessary
age_str = input("Enter your age: ")
try:
age_int = int(age_str)
print(f"You will be {age_int + 1} next year.")
except ValueError:
print("Invalid age entered. Please enter a number.")

Running this script might look like this:

Enter your name: Bob
Hello, Bob!
Enter your age: 25
You will be 26 next year.

Note: The raw_input() function from Python 2 is replaced by input() in Python 3. The old Python 2 input() (which evaluated input) is removed.

Python provides built-in functions and methods for file manipulation.

Before reading or writing, you must open a file using open(). The strongly recommended way is using the with statement, as it ensures the file is automatically closed afterwards, even if errors occur.

with open(file_path, mode='r', encoding=None) as file_object:
# Work with file_object here
pass # File is automatically closed upon exiting the 'with' block

Parameter details:

  • file_path: String containing the path to the file.
  • mode: Determines how the file is opened (e.g., ‘r’ for read, ‘w’ for write, ‘a’ for append, ‘b’ for binary, ’+’ for updating). Default is 'r' (read text).
  • encoding: Specifies the text encoding (e.g., 'utf-8', 'ascii'). Crucial for text files to ensure correct interpretation of characters across different systems. Always specify encoding for text files. 'utf-8' is a common and safe choice.
  • Buffering options also exist but are less commonly changed.

File Modes in Detail:

ModeDescription
rRead (default). Error if file not found.
rbRead in binary mode.
r+Read and write. Pointer at beginning. Error if file not found.
rb+Read and write in binary mode. Pointer at beginning.
wWrite. Creates file or truncates existing file.
wbWrite in binary mode. Creates or truncates.
w+Write and read. Creates or truncates.
wb+Write and read in binary mode. Creates or truncates.
aAppend. Creates file if not found. Pointer at end.
abAppend in binary mode. Creates if not found. Pointer at end.
a+Append and read. Creates if not found. Pointer at end for writing.
ab+Append and read in binary mode. Creates if not found. Pointer at end for writing.
xExclusive creation. Error if file exists.
xbExclusive creation in binary mode.
x+Exclusive creation, read and write.
xb+Exclusive creation, read and write in binary mode.

The file object returned by open() has several useful attributes:

AttributeDescription
file.closedBoolean indicating if the file is closed.
file.modeThe mode the file was opened with.
file.nameThe name of the file.
file.encodingThe encoding used for the file (for text modes).
#!/usr/bin/env python3
file_path = "sample.txt"
# Open a file for writing (creates or overwrites)
try:
with open(file_path, "w", encoding="utf-8") as f:
print(f"Name of the file: {f.name}")
print(f"Closed or not : {f.closed}")
print(f"Opening mode : {f.mode}")
print(f"Encoding : {f.encoding}")
f.write("This is a sample line.\n")
# After exiting the 'with' block
print(f"\nFile closed now? {f.closed}") # Note: f is still defined, but file is closed
except IOError as e:
print(f"Error opening or writing file: {e}")

This produces a result similar to:

Name of the file: sample.txt
Closed or not : False
Opening mode : w
Encoding : utf-8
File closed now? True

The close() Method (and why with is better)

Section titled “The close() Method (and why with is better)”

File objects have a close() method. If you open a file without the with statement, you must explicitly call close() to ensure data is written to disk and resources are released. However, forgetting to close or encountering errors before closing can lead to problems.

Manual closing (less recommended):

# Manual open/close - Avoid if possible
f = None # Initialize to None
try:
f = open("old_style.txt", "w", encoding="utf-8")
print(f"File name: {f.name}")
f.write("Manual close example.\n")
finally:
if f:
f.close() # Must ensure close() is called
print("File closed manually.")

Using the with statement is the standard and safest practice in modern Python.

File objects provide methods for reading and writing data.

Writes a string (in text mode) or bytes (in binary mode) to the file. It does not automatically add newline characters (\n). Returns the number of characters/bytes written.

file_object.write(data)
#!/usr/bin/env python3
file_path = "output.txt"
try:
with open(file_path, "w", encoding="utf-8") as f:
num_chars = f.write("Python is a great language.\n")
print(f"Wrote {num_chars} characters.")
f.write("Yes, it's great!!\n")
print(f"Successfully wrote to {file_path}")
except IOError as e:
print(f"Error writing file: {e}")
# Content of output.txt will be:
# Python is a great language.
# Yes, it's great!!

Reads data from the file. In text mode, reads characters; in binary mode, reads bytes.

file_object.read(size=-1)

Here, size is the optional number of characters/bytes to read. If omitted or negative, it reads until the end of the file.

#!/usr/bin/env python3
file_path = "output.txt"
try:
with open(file_path, "r", encoding="utf-8") as f:
# Read first 10 characters
first_part = f.read(10)
print(f"First 10 chars: '{first_part}'")
# Read the rest of the file
rest_of_file = f.read()
print(f"Rest of file: '{rest_of_file}'")
print("\nReading entire file at once:")
with open(file_path, "r", encoding="utf-8") as f:
all_content = f.read()
print(f"'{all_content}'")
except FileNotFoundError:
print(f"Error: File '{file_path}' not found.")
except IOError as e:
print(f"Error reading file: {e}")

This produces the following result:

First 10 chars: 'Python is '
Rest of file: 'a great language.\nYes, it's great!!\n'
Reading entire file at once:
'Python is a great language.\nYes, it's great!!\n'
  • readline(): Reads one line, including the trailing \n (or until EOF).
  • readlines(): Reads all lines into a list of strings, each including its \n.
print("\nReading with readline():")
try:
with open(file_path, "r", encoding="utf-8") as f:
line1 = f.readline()
line2 = f.readline()
print(f"Line 1: {line1.strip()}") # .strip() removes leading/trailing whitespace
print(f"Line 2: {line2.strip()}")
except FileNotFoundError:
print(f"Error: File '{file_path}' not found.")

tell(): Returns the current position (byte offset from the beginning) of the file pointer.

seek(offset, whence=0): Moves the file pointer. offset is the number of bytes to move. whence determines the reference point:

  • 0 (default): Absolute position from the start of the file.
  • 1: Relative position from the current pointer location.
  • 2: Relative position from the end of the file (offset usually negative).
#!/usr/bin/env python3
file_path = "output.txt"
try:
with open(file_path, "r+", encoding="utf-8") as f: # Open for reading and writing
# Read first 10 characters
str_data = f.read(10)
print(f"Read String is : '{str_data}'")
# Check current position
position = f.tell()
print(f"Current file position : {position}")
# Reposition pointer to the beginning
f.seek(0, 0)
str_data = f.read(10) # Read again
print(f"Again read String is : '{str_data}'")
# Go to the end and write
f.seek(0, 2) # Move to end
f.write("Appending using seek.\n")
except FileNotFoundError:
print(f"Error: File '{file_path}' not found.")
except IOError as e:
print(f"Error accessing file: {e}")

This produces the following result:

Read String is : 'Python is '
Current file position : 10
Again read String is : 'Python is '

Modules like os and pathlib provide functions for file system tasks beyond reading/writing file content.

Provides a platform-independent way to interact with the OS.

Renaming Files: os.rename(current_name, new_name)

Section titled “Renaming Files: os.rename(current_name, new_name)”
import os
# Create a dummy file first
with open("test1.txt", "w") as f: f.write("dummy")
try:
os.rename("test1.txt", "test2.txt")
print("Renamed test1.txt to test2.txt")
except OSError as e:
print(f"Error renaming file: {e}")

Removing Files: os.remove(file_name) or os.unlink(file_name)

Section titled “Removing Files: os.remove(file_name) or os.unlink(file_name)”
try:
os.remove("test2.txt")
print("Removed test2.txt")
except OSError as e:
print(f"Error removing file: {e}")

Offers an object-oriented interface to file paths, often leading to more readable code.

from pathlib import Path
# Create a dummy file
dummy_file = Path("pathlib_test1.txt")
dummy_file.write_text("dummy content", encoding="utf-8")
new_file_path = Path("pathlib_test2.txt")
new_dir_path = Path("my_test_dir")
# Rename
try:
dummy_file.rename(new_file_path)
print(f"Renamed {dummy_file} to {new_file_path}")
except OSError as e:
print(f"Error renaming: {e}")
# Remove
try:
new_file_path.unlink() # Use unlink() for files
print(f"Removed {new_file_path}")
except OSError as e:
print(f"Error removing: {e}")
# Create Directory
try:
new_dir_path.mkdir(exist_ok=True) # exist_ok=True avoids error if exists
print(f"Created directory {new_dir_path}")
except OSError as e:
print(f"Error creating directory: {e}")
# Get Current Directory
print(f"Current directory: {Path.cwd()}")
# Remove Directory (must be empty)
try:
new_dir_path.rmdir() # Use rmdir() for directories
print(f"Removed directory {new_dir_path}")
except OSError as e:
print(f"Error removing directory (may not be empty): {e}")

Both os and pathlib provide functions to manage directories.

os.mkdir(path) or Path(path).mkdir()

os.chdir(path) (Changes the current working directory for the process)

os.getcwd() or Path.cwd()

os.rmdir(path) or Path(path).rmdir() (Directory must be empty)

To remove directories with content, use shutil.rmtree(path) (use with caution!).

  • Built-in open(): For reading/writing file content (use with with).
  • os module: Traditional, function-based approach for OS interactions (paths, processes). os.path submodule for path manipulation.
  • pathlib module: Modern, object-oriented approach for path manipulation and basic file operations. Often preferred.
  • shutil module: For higher-level file operations (copying, moving, removing directory trees).