Short answer

Use with open(path, encoding="utf-8") as f:. The with block closes the file automatically, even if an exception is raised.

python
with open("lessons.txt", encoding="utf-8") as f:
    content = f.read()

#Line by line, without loading the whole file

python
with open("big.log", encoding="utf-8") as f:
    for line in f:
        process(line.rstrip("\n"))

Iterating the file object reads one line at a time. f.readlines() loads everything into memory — fine for a small config file, fatal for a multi-gigabyte log.

#Writing

python
with open("out.txt", "w", encoding="utf-8") as f:
    f.write("first line\n")
    f.writelines(["second\n", "third\n"])

Modes: "r" read (default), "w" write and truncate, "a" append, "x" create and fail if it exists. Add "b" for binary.

#Always pass encoding

Without it, Python uses a platform-dependent default — UTF-8 on most Linux systems, often cp1252 on Windows. That is how you end up with code that works on your machine and mangles accented characters on someone else's.

#JSON and CSV have their own tools

python
import json, csv

with open("data.json", encoding="utf-8") as f:
    data = json.load(f)

with open("rows.csv", newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        print(row["title"])

Note newline="" for CSV — the csv module handles line endings itself, and omitting it produces blank rows on Windows.