Back to Blog
Working with Files in Python: Text Persistence with Pathlib

Working with Files in Python: Text Persistence with Pathlib

October 30, 2026
Updated October 30, 2026
Reynov Christian

Up until now, every program you have written in this series has had a serious limitation: as soon as the script finished running, all the data disappeared. Variables, lists, dictionaries, everything vanished from memory. That is fine for learning exercises, but real applications need to remember things. They need to save user preferences, store notes, keep logs, or load configuration files.

This is where file handling becomes essential. In this article you will learn how to read from and write to text files the modern Python way, using the powerful pathlib module together with the classic open() function and context managers. By the end, you will build a practical Terminal Note-Taking and Daily Journaling App that actually saves your notes to disk.

Why Programs Need Persistent Storage

Computer memory (RAM) is fast but temporary. When your program ends, or when the computer restarts, everything stored only in variables is gone. Files give your programs a permanent place to store information. Whether you are building a simple notes app, a configuration system, or a log of daily activities, files are the foundation of data persistence.

Understanding Paths: Absolute vs Relative

Before you can open a file, Python needs to know exactly where it is located. That location is called a path.

  • Absolute path: the full address starting from the root of the drive (example: C:/Users/Name/Documents/notes.txt on Windows or /home/user/notes.txt on Linux/macOS).
  • Relative path: the location relative to the folder where your script is currently running (example: notes.txt or data/notes.txt).

Relative paths are almost always preferred for projects. They keep your code portable. If you move the entire project folder to another computer, relative paths will still work correctly.

Modern Path Handling with pathlib

Older tutorials often use the os.path module and string concatenation to build paths. That approach works, but it is error-prone and looks different on Windows versus Linux. Modern Python strongly prefers the built-in pathlib module.

pathlib treats paths as objects instead of plain strings. This gives you clean methods and works the same way on every operating system.

from pathlib import Path

# Current working directory
current_dir = Path.cwd()
print(f"Working directory: {current_dir}")

# Home directory of the current user
home = Path.home()
print(f"Home directory: {home}")

# Build a path using the / operator (works on every OS)
notes_path = current_dir / "notes" / "daily.txt"
print(notes_path)

# Useful checks
print(notes_path.exists())      # Does this path exist?
print(notes_path.is_file())     # Is it a file?
print(notes_path.is_dir())      # Is it a directory?
print(notes_path.parent)        # The parent folder
print(notes_path.name)          # Filename with extension
print(notes_path.stem)          # Filename without extension
print(notes_path.suffix)        # The extension (.txt)

The / operator is one of the nicest features of pathlib. You never have to worry about backslashes or forward slashes again.

Creating Folders Safely

Before writing files into a folder, it is good practice to make sure the folder exists. pathlib makes this very easy:

from pathlib import Path

notes_dir = Path("notes")
notes_dir.mkdir(parents=True, exist_ok=True)

parents=True creates any missing parent folders. exist_ok=True prevents an error if the folder already exists. This combination is the modern best practice.

Reading and Writing Files Safely with the with Statement

The golden rule of file handling in Python is: always use a with statement. It guarantees that the file is properly closed even if an error occurs in the middle of reading or writing.

from pathlib import Path

file_path = Path("sample.txt")

# Write mode ("w") creates the file or overwrites it
with open(file_path, "w", encoding="utf-8") as file:
    file.write("Hello from Python!\n")
    file.write("This is the second line.\n")

# Read mode ("r")
with open(file_path, "r", encoding="utf-8") as file:
    content = file.read()          # Read the entire file
    print(content)

# Better for large files: read line by line
with open(file_path, "r", encoding="utf-8") as file:
    for line in file:
        print(line.strip())

Always specify encoding="utf-8". Without it, Python uses the system default encoding, which can cause subtle bugs when your code runs on different computers.

Reading the Whole File vs. Line by Line

So far you have seen .read(), which gives you the whole file as one big string, and looping over the file, which gives you one line at a time. There is a third option: .readlines(). It returns a list where every item is one line of the file.

from pathlib import Path

path = Path("sample.txt")

with open(path, "r", encoding="utf-8") as file:
    lines = file.readlines()   # ["Hello from Python!\n", "This is the second line.\n"]

print(f"The file has {len(lines)} lines")
print(lines[0].strip())        # First line without the trailing newline

Use .read() when you just need the text. Use .readlines() when you want to count lines or pick specific ones. Stick with the plain for line in file loop for big files, because it never loads the whole file into memory. Also remember that every line keeps its \n at the end, so call .strip() before you print or compare it.

File Modes You Need to Know

  • "r": Read (default). Raises an error if the file does not exist.
  • "w": Write. Creates a new file or completely overwrites an existing one.
  • "a": Append. Adds new content to the end of the file without deleting what is already there.
  • "x": Exclusive creation. Fails if the file already exists (useful when you want to avoid accidental overwrites).

Convenient Shortcuts in pathlib

For simple text files, pathlib offers very clean shortcuts that handle opening and closing for you:

from pathlib import Path

path = Path("quick_note.txt")

# Write the entire content in one line
path.write_text("This is a quick note saved the modern way.\n", encoding="utf-8")

# Read the entire content in one line
content = path.read_text(encoding="utf-8")
print(content)

These methods are perfect for small configuration files, notes, or short logs. For larger files or when you need more control, stick with the classic with open(...) approach.

Practical Project: Terminal Note-Taking and Daily Journaling App

Let us put everything together into a useful mini-application. This program lets you write daily journal entries, save them permanently, and review past notes, all from the terminal.

from pathlib import Path
from datetime import datetime

def get_journal_path() -> Path:
    """Return the path to today's journal file."""
    journal_dir = Path("journal")
    journal_dir.mkdir(exist_ok=True)
    today = datetime.now().strftime("%Y-%m-%d")
    return journal_dir / f"{today}.txt"

def add_entry(text: str) -> None:
    """Append a new timestamped entry to today's journal."""
    path = get_journal_path()
    timestamp = datetime.now().strftime("%H:%M:%S")
    entry = f"[{timestamp}] {text}\n"

    with open(path, "a", encoding="utf-8") as file:
        file.write(entry)

    print("Entry saved successfully!")

def show_today() -> None:
    """Display all entries from today."""
    path = get_journal_path()
    if not path.exists():
        print("No entries for today yet.")
        return

    print(f"\n=== Journal for {path.stem} ===")
    print(path.read_text(encoding="utf-8"))

def main() -> None:
    print("=== Daily Journal ===")
    print("1. Add a new entry")
    print("2. Show today's entries")
    print("3. Exit")

    while True:
        choice = input("\nChoose an option (1-3): ").strip()

        if choice == "1":
            text = input("Write your note: ").strip()
            if text:
                add_entry(text)
            else:
                print("Empty note ignored.")
        elif choice == "2":
            show_today()
        elif choice == "3":
            print("Goodbye! Your notes are safely saved.")
            break
        else:
            print("Please choose 1, 2, or 3.")

if __name__ == "__main__":
    main()

This project demonstrates several important ideas at once:

  • Creating folders safely with mkdir(exist_ok=True)
  • Building clean paths with pathlib
  • Appending data without overwriting previous content
  • Using type hints and small, focused functions
  • Separating the main menu logic from the file operations

Common Beginner Mistakes

  • Forgetting the with statement: files may stay open and lock resources.
  • Using "w" when you meant "a": this silently deletes everything that was already in the file.
  • Hardcoding Windows-style backslashes: "C:\notes\new_file.txt" can cause strange errors because Python reads the \n in \new_file.txt as a newline character. Prefer pathlib or forward slashes.
  • Ignoring encoding: always write encoding="utf-8" for text files.
  • Not checking whether a folder exists before writing into it.

Try It Yourself

  1. Extend the journal app so the user can also view entries from a specific date (for example, enter 2026-10-29).
  2. Add a feature that counts how many entries exist for the current day.
  3. Create a small script that lists all journal files inside the journal folder using Path.glob("*.txt").

Frequently Asked Questions

Should I still learn os.path?

You will still see it in older code, but for new projects you should prefer pathlib. It is cleaner, more readable, and officially recommended.

When should I use path.write_text() versus open()?

Use write_text() and read_text() for small, simple text files. Use the classic with open(...) approach when you need more control (appending, reading line by line, binary mode, etc.).

What happens if the file does not exist when I try to read it?

Python raises a FileNotFoundError. You can either check path.exists() first or wrap the read operation in a try...except FileNotFoundError block (the more Pythonic style).

Is it safe to use relative paths in real projects?

Yes, as long as you are careful about the current working directory. Many professional applications still prefer relative paths inside their own project structure.

Does Python 3.14 change anything important for file handling?

pathlib received a few convenient additions in recent versions (such as easier copy and move methods), but the core practices you learned in this article remain the current best practice.

Your programs can now remember things between runs, and that was the last big puzzle piece of Phase 1. In the next article we will bring everything together in a Phase 1 capstone: a command-line inventory and sales system that combines lists, dictionaries, functions, file persistence, and error handling into one real app.

Learning programming works best as a collaborative process, and your questions and insights help make these tutorials more effective for everyone. If you get stuck with file paths, run into a FileNotFoundError you can’t explain, or come up with a creative upgrade for the journaling app, please share your experience in the comments. Your feedback helps create a learning community where we can all benefit from each other’s discoveries and solutions.

You Might Also Like

Comments

Loading comments…

Leave a Comment

0/5000