Reading Files and Iterating Over Lines
Parsing is the process of finding interesting lines in a file and extracting specific pieces of information from them.
Try it: Reading and Writing Text Files
How open() returns a file object with a position pointer, how read(), read(n), readline(), readlines() and for line in fh advance it (and return '' at the end), how 'w' and 'a' modes change the file, and why a closed file (after close() or a with block) can no longer be read.
How it works
- open(name, 'r') starts the pointer at 0; 'w' empties the file first; 'a' starts at the end.
- read() returns everything from the pointer to the end; read(n) at most n characters; readline() up to and including the next \n; each moves the pointer past what it returned.
- At the end of the file every read returns '' (an empty string), which is how loops know to stop; for line in fh reads line by line the same way.
- write() text waits in a buffer and reaches the file when it is closed (or the program ends); reading a 'w'/'a' file or writing an 'r' file raises io.UnsupportedOperation.
- close(), or leaving a with block, closes the file; any further read raises ValueError: I/O operation on closed file.
Default run (10 steps): Ready: notes.txt holds 4 lines (51 characters). Nothing is open yet. … Finished. notes.txt now holds "From: stephen\nSubject: meeting\nFrom: louis\nThanks!\n".
Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. One virtual text file with \n line endings (as on Linux/macOS), positions counted in characters (ASCII only), and a write buffer that is only flushed by close() or the end of the program.
Loading the simulation…
Finding Useful Lines
A file often contains more information than a program needs. An email log, for example, may contain thousands of lines, but a program might need only the day of the week from lines that begin with From. Parsing is the process of finding those interesting lines and extracting specific pieces of information from them.
Parsing follows a filter-then-parse pattern. First, check whether a line has the structure you expect. Then, split that line into fields and select the field you need. This order matters because not every line in a file has the same structure.
Filtering with startswith
The startswith() method checks whether a line begins with a particular string. For the email log pattern, checking line.startswith('From ') selects lines that begin with From followed by a space. The space is significant because it helps distinguish the expected line structure from a line that merely contains the word From somewhere else.
The loop reads one line at a time. For each line, the conditional check has two possible results. If the line starts with From followed by a space, the indented code runs. If it does not, the line is skipped and the loop moves to the next line.
Turning Lines into Fields
After a line passes the filter, split() breaks the line into a list of words or fields. Each resulting list element has a position called an index. Indexing the list lets you select one exact field instead of using the entire line.
Locating Fields in a From Line
A matching line has the structure From sender day month time year. Which index contains the sender, day, and month?
- 1Split the line
The line is transformed into a list whose elements are the separate words or fields.
- 2Count from index zero
The first field is at index 0, so From is words[0] and the sender is words[1].
- 3Select the needed field
The day is at words[2], while the month is at words[3].
Use words[1] for the sender, words[2] for the day, and words[3] for the month.
line = 'From sender@example.com Tue Jan 14:20 2025'
words = line.split()
day = words[2]
print(day)- 1The string represents a line with the expected From structure.
- 2
split()creates a list of fields from the line. - 3words[2] selects the field at index 2, which is the day.
- 4The selected day is printed.
TueCombining the Two Steps
The complete pattern reads each line, tests its beginning, splits only matching lines, and extracts the desired field. In this example, the desired field is the day of the week at index 2.
Tue
Fri
MonMistakes to Avoid
Common mistake Splitting every line before checking its structure
What it looks likewords = line.split() is executed before testing whether the line starts with From.Why it failsFiles can contain headers, comments, blank lines, or other formats that do not contain the expected fields.
Do this insteadUse startswith() first, then split only the lines that match.
Common mistake Filtering for From without the trailing space
What it looks likeline.startswith('From')Why it failsThis can match lines that begin with the word From but do not have the expected From line structure.
Do this insteadUse the expected prefix From followed by a space when that is the structure being parsed.
Common mistake Using the wrong list index
What it looks likeprint(words[1]) when the intended field is the day.Why it failsIn the described From structure, words[1] is the sender and words[2] is the day.
Do this insteadWrite down the fields and count their positions starting at index 0.
Common mistake Ignoring trailing whitespace
What it looks likeUsing a line exactly as read without considering trailing whitespace.
Why it failsTrailing whitespace can affect how a line is handled during parsing.
Do this insteadStrip trailing whitespace when appropriate before applying the rest of the parsing logic.
Practice the Pattern
Suppose a file contains lines with the structure From sender day month time year, mixed with unrelated lines. Write the loop logic that prints only the day from matching lines.
Hints
- Iterate over the file one line at a time.
- Use
startswith('From ')as the conditional check. - Call
split()on a matching line. - The day is at index 2 in this structure.
Change the extraction so that the program prints the sender instead of the day. Which index should you use, and why?
Hints
- The first field From is at index 0.
- The sender is the next field in the described structure.
Key Takeaways
- 1Reading a file in a loop lets the program apply the same checks to one line at a time.
- 2Parsing means finding interesting lines and extracting specific information from them.
- 3Use
startswith()to filter lines before attempting to parse them. - 4Use
split()to turn a matching line into a list of fields. - 5Use list indexing to select the required field, counting positions from index 0.
At a glance
- Parsing uses a two-step filter-then-parse process.
startswith()identifies lines with the expected structure.split()separates a matching line into individual fields.- List indexes provide access to particular fields, such as the day at index 2 in the described From structure.
- Careful filtering and index counting make file-processing code more robust and easier to debug.