Course outline

Python / Foundations / Lesson 6

Files and structured data

Read and write text deliberately, manage resources, and separate storage formats from validation of their contents.

About 5 min readContent reviewed
On this page

Lesson goals

  • Paths are values
  • Failures belong to the boundary

Paths are values

A path identifies a location; it is not the file’s contents. pathlib.Path provides operations for joining names and reading or writing data without manually assembling platform separators. A relative path is interpreted from the process’s current working directory, which may differ from the directory containing the script. Record that distinction when a file cannot be found. Absolute paths can be useful in diagnostics but should not become hard-coded assumptions about another user’s machine. For runnable learning examples, use TemporaryDirectory so all created files belong to an isolated temporary folder that is automatically cleaned up after the example finishes.

Text mode and resource lifetime

Opening a file acquires an operating-system resource. A with statement makes ownership visible and closes the file when its block exits, including when an exception propagates. Specify encoding="utf-8" for text under your control instead of relying on platform defaults. Read mode expects an existing file; write mode replaces existing contents, and append mode adds at the end. Choose deliberately before running. Reading line by line limits how much input is held at once, while read_text is convenient for a known small document. Neither approach validates that the content conforms to the application’s expected structure or size.

Write and read a small text file

The first example owns its temporary directory and names the encoding on both sides of the boundary. splitlines separates logical lines without retaining their newline characters. This is useful for display or simple record processing, but it is not a substitute for a format parser when fields themselves can contain newlines. Keep transformations explicit: here the file contains two lines and the report joins them with a separator. The exact output is independent of the random temporary directory name because the example prints content rather than an environment-dependent path. No existing learner files are modified.

python Write and read a small text file — worked example
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as folder:
    path = Path(folder) / "notes.txt"
    path.write_text("first\nsecond\n", encoding="utf-8")
    lines = path.read_text(encoding="utf-8").splitlines()
    print(" | ".join(lines))
Expected output
first | second

The directory context owns cleanup. Explicit UTF-8 agrees on encoding, and splitlines removes the stored line separators before joining the content.

JSON is a format, not a schema

JSON represents objects, arrays, strings, numbers, Booleans, and null. json.dumps serializes compatible Python data, while json.loads parses text into Python values. JSON object keys are strings; a Python dictionary with nonstring keys may therefore fail to round-trip as originally modeled. Parsed data is not automatically a valid order, user, or configuration. Check expected container types, required fields, allowed values, and size limits before using it. The example round-trips a small object with a Unicode label, then reads fields explicitly. ensure_ascii=False improves readability of non-ASCII characters in the stored text without changing the JSON meaning.

python JSON is a format, not a schema — worked example
import json

record = {"label": "قلم", "quantity": 3}
text = json.dumps(record, ensure_ascii=False)
loaded = json.loads(text)
print(loaded["label"])
print(loaded["quantity"] * 2)
Expected output
قلم
6

The Unicode label survives serialization and parsing. Quantity remains an integer and can be calculated with after the expected fields are accessed.

Failures belong to the boundary

A file operation can fail because a path is missing, permissions are insufficient, or storage is unavailable. A parse operation can fail even after reading succeeds. Keep these stages distinguishable so the message explains what actually happened and the caller can choose a sensible response. Do not catch every exception and silently substitute empty data; that can turn corruption into a convincing but false report. For important writes, later study temporary sibling files and atomic replacement on the target filesystem. TemporaryDirectory in this lesson provides isolation for teaching, not a claim that every multi-step write becomes transactional.

Common errors and fixes

FileNotFoundError despite seeing the file near the script

Why it happens
The relative path is resolved from a different working directory.
How to fix it
Inspect the working directory and pass an intentional base path.
Prevent it next time
Make data locations explicit configuration rather than implicit assumptions.

UnicodeDecodeError during reading

Why it happens
The selected codec disagrees with the file’s actual encoding.
How to fix it
Use the encoding specified by the producer and reject malformed data clearly.
Prevent it next time
Document encoding at every text-storage boundary.

A saved report erases an earlier file

Why it happens
Write mode replaces content at the chosen path.
How to fix it
Use an isolated destination and choose overwrite or append intentionally.
Prevent it next time
Review paths and write mode before executing storage code.

Check your understanding

1. Design validation after loading {"quantity": "3"} from JSON.

Show worked solution

Parsing succeeds, but quantity is a string rather than the required integer. Validate the top-level object, require the field, check its type, then require a nonnegative range. Decide explicitly whether textual conversion is allowed; do not let successful JSON parsing imply a valid domain record.

2. Explain how to count lines in a large UTF-8 file without loading it all.

Show worked solution

Open the path with a with block and encoding="utf-8", then initialize a count and increment it while iterating over the file. Each iteration yields a line and only a small portion is needed at once. Document that this counts logical lines as yielded, including a final line without a trailing newline.

Key takeaways

  • Manage file lifetime with a context manager.
  • Specify encoding and write policy deliberately.
  • Parsing and domain validation are separate responsibilities.

Official references