Skip to content

Python 3 - Files I/O

This chapter covers the essential functions and techniques for reading from and writing to files in Python 3.

The print() function is the primary way to display output to the standard output stream (usually your terminal/console).

#!/usr/bin/env python3
print("Hello, world!")
name = "Alice"
age = 30
print("Name:", name, "Age:", age) # Multiple arguments separated by spaces by default
print(f"Name: {name}, Age: {age}") # Using f-string for formatted output
print("No newline at the end ->", end="") # Use 'end' to change the ending character
print("<- See?")
print("Item1", "Item2", sep=" | ") # Use 'sep' to change the separator

Output:

Hello, world!
Name: Alice Age: 30
Name: Alice, Age: 30
No newline at the end -><- See?
Item1 | Item2

The input() function reads a line of text entered by the user from standard input (the keyboard) and returns it as a string.

Note: Python 2 had raw_input() (returned string) and input() (tried to evaluate input as Python code). In Python 3, input() behaves like Python 2’s raw_input(), always returning a string. You must explicitly convert the input if you need a different type (e.g., number).

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

Python provides robust tools for interacting with files on the filesystem.

Section titled “Opening Files: open() and with Statement (Recommended)”

Before reading or writing, you must open a file using the built-in open() function. It returns a file object.

Best Practice: Use the with statement when working with files. It ensures the file is automatically closed even if errors occur during processing.

with open(file_path, mode='r', encoding=None) as file_variable:
# Work with the file object (file_variable)
pass
# File is automatically closed here

Parameters for open():

  • file_path: A string containing the path to the file (e.g., 'my_data.txt', '../config/settings.ini').
  • mode: A string indicating how the file should be opened (read, write, append, etc.). Defaults to 'r' (read text). See table below.
  • encoding: Specifies the encoding used to decode/encode the file (e.g., 'utf-8', 'latin-1'). Crucial for text modes. System default is used if None, but explicitly setting 'utf-8' is often recommended for portability.
ModeDescription
'r'Read (default). Text mode. Error if file doesn’t exist.
'w'Write. Text mode. Creates file if it doesn’t exist, truncates (empties) if it does.
'a'Append. Text mode. Creates file if it doesn’t exist. Writes are added to the end.
'rb'Read Binary. For non-text files (images, executables).
'wb'Write Binary. Truncates if file exists.
'ab'Append Binary.
'r+'Read and Write. Text mode. Doesn’t truncate. Error if file doesn’t exist.
'w+'Write and Read. Text mode. Truncates if file exists.
'a+'Append and Read. Text mode. Writes to the end.
'x'Exclusive Creation. Text mode. Fails if the file already exists.
'xb', 'x+', etc.Binary or Read/Write variations of exclusive creation.

Note: Appending b to the mode (e.g., 'rb', 'wb') signifies binary mode. Text mode (t) is the default if b is not specified.

Once opened, the file object has useful attributes:

AttributeDescription
file.closedBoolean: True if the file is closed, False otherwise.
file.modeString: The mode the file was opened with (e.g., 'r', 'wb').
file.nameString: The name/path of the file.
file.encodingString: The encoding used (in text mode).
#!/usr/bin/env python3
try:
with open("sample.txt", "w", encoding="utf-8") as f:
print(f"File Name: {f.name}")
print(f"Opening Mode: {f.mode}")
print(f"Encoding: {f.encoding}")
print(f"Is Closed? {f.closed}")
f.write("Test line\n")
print(f"After with block, Is Closed? {f.closed}")
except IOError as e:
print(f"Error opening/writing file: {e}")

Use the write() method to write a string (in text mode) or bytes (in binary mode) to the file. It returns the number of characters/bytes written. Remember to include newline characters (\n) manually in text mode if needed.

The writelines() method writes a sequence (e.g., a list) of strings to the file. It does not add newline characters between the strings.

#!/usr/bin/env python3
lines_to_write = [
"First line.\n",
"Second line.\n",
"Third line without newline."
]
try:
with open("output.txt", "w", encoding="utf-8") as f:
num_chars = f.write("This is a single line written with write().\n")
print(f"Wrote {num_chars} characters.")
f.writelines(lines_to_write)
print("Wrote multiple lines with writelines().")
except IOError as e:
print(f"Error writing to file: {e}")

Reading from Files (read, readline, readlines, iteration)

Section titled “Reading from Files (read, readline, readlines, iteration)”

Several methods exist for reading:

  • read([size]): Reads size characters/bytes from the file. If size is omitted or negative, reads the entire file content.
  • readline(): Reads a single line from the file, including the newline character (\n) at the end. Returns an empty string at the end of the file.
  • readlines(): Reads all remaining lines from the file and returns them as a list of strings.
  • Iteration: The most Pythonic way to read a file line by line is to iterate directly over the file object.
#!/usr/bin/env python3
# Assume 'output.txt' exists from the previous example
try:
# Method 1: Read entire file
with open("output.txt", "r", encoding="utf-8") as f:
content = f.read()
print("--- Read All ---")
print(content)
# Method 2: Read line by line using iteration (Recommended)
with open("output.txt", "r", encoding="utf-8") as f:
print("--- Read by Iteration ---")
for line in f:
print(line, end='') # print adds newline, so end='' prevents double spacing
print("\n-------------------------")
# Method 3: Readlines into a list
with open("output.txt", "r", encoding="utf-8") as f:
lines_list = f.readlines()
print("--- Readlines List ---")
print(lines_list) # List includes newline characters
except FileNotFoundError:
print("Error: output.txt not found.")
except IOError as e:
print(f"Error reading file: {e}")

tell(): Returns the current position of the file pointer (cursor) as an integer number of bytes from the beginning.

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

  • 0 (default): From the beginning of the file (os.SEEK_SET).
  • 1: Relative to the current position (os.SEEK_CUR).
  • 2: Relative to the end of the file (os.SEEK_END).

Note: Seeking in text mode (other than seek(0)) can be unreliable unless you are seeking to positions previously returned by tell(). Binary mode offers more predictable seeking by byte offset.

#!/usr/bin/env python3
try:
with open("output.txt", "r+", encoding="utf-8") as f: # r+ allows reading and writing
text = f.read(10)
print(f"Read first 10 chars: '{text}'")
current_pos = f.tell()
print(f"Current position: {current_pos}")
f.seek(0) # Go back to the beginning
print(f"Position after seek(0): {f.tell()}")
f.seek(5, 1) # Move 5 bytes forward from current position (which is 0)
print(f"Position after seek(5, 1): {f.tell()}")
print(f"Reading from new pos: '{f.readline().strip()}'")
# Overwrite starting from the beginning
f.seek(0)
f.write("OVERWRITTEN!")
except FileNotFoundError:
print("Error: output.txt not found.")
except IOError as e:
print(f"Error seeking/reading file: {e}")

File and Directory Management (os and pathlib)

Section titled “File and Directory Management (os and pathlib)”

Python provides modules for interacting with the filesystem beyond just reading/writing file contents.

  • os module: The traditional module for interacting with the operating system, including file operations.
  • pathlib module (Python 3.4+): A modern, object-oriented approach to filesystem paths and operations. Often preferred for clarity and ease of use.

We’ll show examples using both where applicable.

#!/usr/bin/env python3
import os
from pathlib import Path
# Create a dummy file
with open("test_old.txt", "w") as f:
f.write("temporary file")
# --- Using os ---
try:
os.rename("test_old.txt", "test_new_os.txt")
print("Renamed using os")
os.remove("test_new_os.txt")
print("Removed using os")
except OSError as e:
print(f"OS Error: {e}")
# Recreate for pathlib example
with open("test_old.txt", "w") as f:
f.write("temporary file")
# --- Using pathlib ---
try:
old_path = Path("test_old.txt")
new_path_pl = Path("test_new_pathlib.txt")
old_path.rename(new_path_pl)
print("Renamed using pathlib")
new_path_pl.unlink() # pathlib uses unlink() for deleting files/symlinks
print("Removed using pathlib")
except OSError as e:
print(f"Pathlib Error: {e}")

Creating, changing, getting the current directory, and removing directories.

#!/usr/bin/env python3
import os
from pathlib import Path
# --- Using os ---
new_dir_os = "my_temp_dir_os"
try:
print(f"Current directory (os): {os.getcwd()}")
os.mkdir(new_dir_os) # Create directory
print(f"Created directory: {new_dir_os}")
os.chdir(new_dir_os) # Change into the new directory
print(f"Current directory after chdir (os): {os.getcwd()}")
os.chdir("..") # Change back up
print(f"Current directory after chdir .. (os): {os.getcwd()}")
os.rmdir(new_dir_os) # Remove directory (must be empty)
print(f"Removed directory: {new_dir_os}")
except OSError as e:
print(f"OS Error: {e}")
# Clean up if error occurred mid-way
if os.path.exists(new_dir_os):
if os.path.basename(os.getcwd()) == new_dir_os: os.chdir("..")
os.rmdir(new_dir_os)
print("\n---\n")
# --- Using pathlib ---
new_dir_pl = Path("my_temp_dir_pl")
try:
print(f"Current directory (pathlib): {Path.cwd()}")
new_dir_pl.mkdir() # Create directory
print(f"Created directory: {new_dir_pl}")
# pathlib doesn't have a direct chdir, use os.chdir()
os.chdir(new_dir_pl)
print(f"Current directory after chdir (pathlib): {Path.cwd()}")
os.chdir("..")
print(f"Current directory after chdir .. (pathlib): {Path.cwd()}")
new_dir_pl.rmdir() # Remove directory (must be empty)
print(f"Removed directory: {new_dir_pl}")
except OSError as e:
print(f"Pathlib Error: {e}")
# Clean up if error occurred mid-way
if new_dir_pl.exists():
if Path.cwd().name == new_dir_pl.name: os.chdir("..")
new_dir_pl.rmdir()

Additional useful functions/methods:

  • os.makedirs(path, exist_ok=False) / Path(path).mkdir(parents=True, exist_ok=False): Creates directories recursively (including intermediate ones).
  • os.path.exists(path) / Path(path).exists(): Checks if a path exists.
  • os.path.isfile(path) / Path(path).is_file(): Checks if path is a regular file.
  • os.path.isdir(path) / Path(path).is_dir(): Checks if path is a directory.
  • os.listdir(path) / Path(path).iterdir(): Lists contents of a directory.