Quiz: File Input and Output

Test your understanding before moving on. Target: 70% or higher to proceed confidently.


Section 1: Multiple Choice (1 point each)

1. What does the "r" mode do when passed to open()?

  • A) Creates a new file for reading
  • B) Opens an existing file for reading (error if file doesn't exist)
  • C) Opens a file for reading, creating it if it doesn't exist
  • D) Opens a file for both reading and writing
Answer **B)** Opens an existing file for reading (error if file doesn't exist). *Why B:* Read mode requires the file to exist. If it doesn't, Python raises `FileNotFoundError`. *Why not A:* `"r"` does not create files — that's `"w"`, `"a"`, or `"x"`. *Why not C:* Only write and append modes create files automatically. *Why not D:* For both reading and writing, you'd need `"r+"`. *Reference:* Section 10.2

2. What happens to an existing file's content when you open it in "w" mode?

  • A) The content is preserved, and new data is added at the end
  • B) The content is preserved, and new data is added at the beginning
  • C) The content is immediately erased, even before you write anything
  • D) The content is backed up and then erased
Answer **C)** The content is immediately erased, even before you write anything. *Why C:* Write mode truncates the file to zero length the moment `open()` is called. This is one of the most common sources of accidental data loss. *Why not A:* That describes append mode (`"a"`). *Why not B:* No standard mode inserts at the beginning. *Why not D:* Python does not create automatic backups. *Reference:* Section 10.4

3. Which of the following is the PRIMARY benefit of using with open(...) as f: instead of f = open(...)?

  • A) It makes the code run faster
  • B) It allows reading and writing simultaneously
  • C) It guarantees the file is closed, even if an error occurs
  • D) It automatically detects the file encoding
Answer **C)** It guarantees the file is closed, even if an error occurs. *Why C:* The `with` statement uses a context manager that calls `f.close()` when the block exits, whether normally or due to an exception. *Why not A:* There is no performance difference. *Why not B:* The mode determines read/write access, not `with`. *Why not D:* Encoding must still be specified manually. *Reference:* Section 10.3

4. What does f.readlines() return?

  • A) A single string containing the entire file
  • B) A list of strings, one per line, with newline characters included
  • C) A list of strings, one per line, with newline characters stripped
  • D) A generator that yields one line at a time
Answer **B)** A list of strings, one per line, with newline characters included. *Why B:* `readlines()` returns something like `["line 1\n", "line 2\n", "line 3\n"]`. The `\n` characters are part of the string. *Why not A:* That's what `read()` returns. *Why not C:* Newlines are preserved. You must call `.strip()` to remove them. *Why not D:* `readlines()` loads everything into memory at once. Iterating over the file object directly (`for line in f:`) is the lazy/generator-like approach. *Reference:* Section 10.2

5. Which pathlib operation joins path components in a cross-platform way?

  • A) Path("dir") + "file.txt"
  • B) Path("dir") / "file.txt"
  • C) Path("dir").join("file.txt")
  • D) Path.concat("dir", "file.txt")
Answer **B)** `Path("dir") / "file.txt"` *Why B:* The `/` operator is overloaded for `Path` objects. It joins components using the correct separator for the current operating system. *Why not A:* `+` on a `Path` object doesn't concatenate path components — it raises a `TypeError`. *Why not C:* `Path` has no `.join()` method (the `os.path.join()` function is the older approach). *Why not D:* There is no `Path.concat()` method. *Reference:* Section 10.6

6. When reading a CSV file in Python, why should you pass newline="" to open()?

  • A) To make the file open faster
  • B) To prevent Python's universal newline handling from interfering with the csv module
  • C) To automatically strip newlines from each row
  • D) To convert Windows line endings to Unix line endings
Answer **B)** To prevent Python's universal newline handling from interfering with the csv module. *Why B:* The `csv` module has its own newline handling. If Python also translates newlines, the two systems can conflict, causing blank rows or corrupted data. *Why not A:* `newline=""` does not affect performance. *Why not C:* The csv module handles row parsing regardless. *Why not D:* That's what default newline handling does — but for CSV files, we want to disable it and let the csv module handle everything. *Reference:* Section 10.7

7. What is the difference between json.dump() and json.dumps()?

  • A) dump() is for dictionaries; dumps() is for lists
  • B) dump() writes to a file object; dumps() returns a string
  • C) dump() is faster; dumps() is safer
  • D) dump() uses compact format; dumps() uses pretty format
Answer **B)** `dump()` writes to a file object; `dumps()` returns a string. *Why B:* The "s" in `dumps` stands for "string." `json.dump(obj, file)` writes directly to a file. `json.dumps(obj)` returns a JSON-formatted string. *Why not A:* Both work with any JSON-serializable Python object. *Why not C:* Both are equally safe and perform similarly. *Why not D:* Both support the `indent` parameter for pretty printing. *Reference:* Section 10.8

8. What Python type does None become when serialized to JSON and then deserialized back?

  • A) The string "None"
  • B) The integer 0
  • C) Python None
  • D) Python False
Answer **C)** Python `None`. *Why C:* Python `None` maps to JSON `null`, and JSON `null` maps back to Python `None`. The round-trip is faithful. *Why not A:* JSON `null` is not a string — it's a distinct value. *Why not B:* JSON `null` is not a number. *Why not D:* JSON has separate `null`, `false`, and `true` values. *Reference:* Section 10.8

Section 2: True or False (1 point each)

9. write() automatically adds a newline at the end of the string you pass to it.

Answer **False.** Unlike `print()`, `write()` does NOT add a newline. You must include `\n` explicitly. *Reference:* Section 10.4

10. Iterating over a file object with for line in f: loads the entire file into memory.

Answer **False.** Iterating over a file object reads one line at a time. This is the memory-efficient approach for processing large files. *Reference:* Section 10.5

11. csv.DictReader requires you to manually specify the column names.

Answer **False.** `DictReader` automatically uses the first row of the CSV as the column names (dictionary keys). You can override this, but it's not required. *Reference:* Section 10.7

12. Python tuples survive a JSON round-trip — if you dump a tuple and load it back, you get a tuple.

Answer **False.** Python tuples are converted to JSON arrays, which are loaded back as Python lists. The tuple-vs-list distinction is lost during JSON serialization. *Reference:* Section 10.8

Section 3: Short Answer (2 points each)

13. You run this code and get an error:

with open("data/results.csv", "r") as f:
    content = f.read()

Error: FileNotFoundError: [Errno 2] No such file or directory: 'data/results.csv'

List three possible causes and one debugging technique.

Answer **Possible causes:** 1. The file doesn't exist at that path (typo in name or extension). 2. The current working directory is not what you expect — the relative path `data/results.csv` is relative to where the script is *run*, not where it *lives*. 3. The `data/` directory itself doesn't exist. **Debugging technique:** Print `Path.cwd()` to see the actual working directory, then compare it to where the file actually is. Alternatively, use `Path(__file__).parent / "data" / "results.csv"` for a path relative to the script's location. *Reference:* Section 10.9

14. Explain why this code is dangerous:

with open("important_data.txt", "w") as f:
    pass
Answer Opening a file in `"w"` (write) mode immediately truncates (erases) the file. Even though no `write()` call is made inside the block, the file's contents are already gone the moment `open()` executes. The file is now empty. This is dangerous because the data loss happens silently — there is no warning, confirmation, or undo. If `important_data.txt` contained years of data, it's now an empty file. *Reference:* Section 10.4

15. What is the output of this code?

with open("test.txt", "w") as f:
    f.write("abc")
    f.write("def")

with open("test.txt", "r") as f:
    print(repr(f.read()))
Answer
'abcdef'
`write()` does not add newlines between calls. The two strings are concatenated directly. The file contains exactly six characters: `abcdef`. *Reference:* Section 10.4

16. You have a CSV file with 10 million rows. Which approach is better and why?

# Approach A
with open("huge.csv", "r") as f:
    reader = csv.reader(f)
    data = list(reader)   # load everything

# Approach B
with open("huge.csv", "r") as f:
    reader = csv.reader(f)
    for row in reader:
        process(row)
Answer **Approach B** is better. It processes one row at a time, keeping memory usage constant regardless of file size. Approach A loads all 10 million rows into a list, which could consume gigabytes of memory and potentially crash the program. The general rule: iterate over large files line by line (or row by row) rather than loading everything into memory at once. *Reference:* Section 10.5

Section 4: Code Writing (3 points each)

17. Write a function copy_file(source: str, destination: str) -> None that copies the contents of one text file to another. Use with statements.

Answer
def copy_file(source: str, destination: str) -> None:
    """Copy the contents of source to destination."""
    with open(source, "r") as src, open(destination, "w") as dst:
        for line in src:
            dst.write(line)
*Note:* Using line-by-line copying is memory-efficient. You could also use `dst.write(src.read())` for small files.

18. Write a function csv_average(filename: str, column: str) -> float that reads a CSV file and returns the average of the values in the specified column. Use csv.DictReader.

Answer
import csv

def csv_average(filename: str, column: str) -> float:
    """Return the average of a numeric column in a CSV file."""
    total = 0.0
    count = 0
    with open(filename, "r", newline="") as f:
        for row in csv.DictReader(f):
            total += float(row[column])
            count += 1
    return total / count

19. Write a function save_high_scores(scores: list[dict], filename: str) -> None that saves a list of score dictionaries to JSON, and a companion load_high_scores(filename: str) -> list[dict] that loads them back. Each score dict has "player" and "score" keys. load_high_scores should return an empty list if the file doesn't exist.

Answer
import json
from pathlib import Path

def save_high_scores(scores: list[dict], filename: str) -> None:
    """Save high scores to a JSON file."""
    with open(filename, "w") as f:
        json.dump(scores, f, indent=2)

def load_high_scores(filename: str) -> list[dict]:
    """Load high scores from JSON. Returns [] if file missing."""
    path = Path(filename)
    if not path.exists():
        return []
    with open(path, "r") as f:
        return json.load(f)

20. Write a program that reads sample-data.csv and produces a JSON file called employees.json where each employee is a dictionary with all their CSV fields plus a computed "weekly_pay" field. The JSON file should be an array of employee objects, pretty-printed.

Answer
import csv
import json

employees = []
with open("sample-data.csv", "r", newline="") as f:
    for row in csv.DictReader(f):
        row["weekly_pay"] = round(
            float(row["hours_worked"]) * float(row["hourly_rate"]), 2
        )
        employees.append(row)

with open("employees.json", "w") as f:
    json.dump(employees, f, indent=2)

print(f"Converted {len(employees)} employees to JSON.")