Files, folders, paths, plain text, CSV tables, reading and writing data, cleaning, filtering, sorting, summarizing, and the responsible use of data about real people
Every time you save a document, submit a form, purchase something online, or check your grades, a program is reading or writing data stored in a file or database. Variables hold information while a program runs — but the moment the program stops, all of that information vanishes unless it was written somewhere persistent. Files solve this problem. They are the connective tissue that holds real software systems together, and understanding them is one of the most practical skills in computing.
This chapter introduces how programs store, read, write, and process data that persists beyond a single program run. You will learn what files are, how file systems organize them into trees of folders, and how paths describe where any file lives. You will explore plain text files and the CSV format for tabular data — two of the most common data formats in computing. You will trace the read-process-write pattern that most data programs follow, practice cleaning messy data, filter and sort records, and compute summaries that describe datasets as a whole. The chapter closes by addressing the ethical responsibilities that arise whenever programs handle data about real people.
These skills connect directly to databases, data science, web development, and software engineering — all courses that build on this foundation. By the end of this chapter, you should be able to work confidently with files and tables, trace data programs step by step, and think carefully about the human consequences of data decisions.
A school stores records for every student: names, grades, scores, schedules, health notes, and contact information. All of this is saved in files on a server somewhere. When a teacher opens a gradebook, a program reads one of those files. When a student submits an assignment, a program writes to another. When an administrator generates a report, a program filters and summarizes thousands of rows. How does this actually work — and what responsibilities come with the power to read and change data about real students?
When a program runs, it stores information in variables. Variables live in memory — fast storage that holds values only while the program is running. The moment the program stops, all of that information disappears.
Think about what would happen if your word processor worked only with memory. You would type an entire document, close the program, and find nothing saved. Every photo editing app would forget your edits. Every spreadsheet would reset to blank. Games would have no save states. Email clients would show empty inboxes every time they opened.
Programs solve this problem using files. A file is a named collection of data stored on a persistent storage device — a hard drive, solid-state drive, USB drive, or similar medium. Persistent means the data survives when power is turned off.
Almost every useful program you have ever interacted with reads or writes files in some form — documents, photos, music, logs, settings, databases, web pages, source code, and data exports are all stored as files. Understanding files is not optional knowledge in computing. Files are the connective tissue that holds persistent software systems together.
The distinction between memory and storage is worth making precise before going further.
| Property | Memory (RAM) | Storage (Drive) |
|---|---|---|
| Speed | Very fast | Slower |
| Persistence | Temporary (lost on power-off) | Permanent (survives power-off) |
| Typical size | Gigabytes (smaller) | Terabytes (much larger) |
| Cost per unit | More expensive | Less expensive |
| Used for | Active program data, variables | Files, databases, installed programs |
When a program reads a file, it copies data from storage into memory so the CPU can process it quickly. When a program writes a file, it copies data from memory back to storage so it survives after the program ends.
Files do not float in isolation. They are organized using file systems — structures that let operating systems store, find, and manage millions of files efficiently. A folder, also called a directory, is a container for files and other folders. Folders nest inside folders, forming a tree-like structure. At the top of the tree is the root directory — the starting point for the entire file system.
On Windows systems, the root of each drive is identified by a letter followed by a backslash: C:\. On macOS and Linux systems, the root is simply /.
/ (root)
├── home/
│ └── student/
│ ├── documents/
│ │ ├── essay.txt
│ │ └── notes.md
│ ├── data/
│ │ ├── students.csv
│ │ └── scores.csv
│ └── programs/
│ └── grade_calculator.py
└── etc/
└── config.txt
A path is a description of where a file is located in the file system. Paths describe the route from the root (or from the current location) to the target file, moving through folders one level at a time.
Absolute paths start from the root and describe the full location. Relative paths start from the current working directory — wherever the program is currently located. The .. symbol means "go up one level toward the parent folder."
Absolute (Linux / macOS): /home/student/data/students.csv Absolute (Windows): C:\Users\student\data\students.csv Relative (from student/): data/students.csv Relative (from programs/): ../data/students.csv ← go up, then into data/
Most file names include an extension — a short suffix after a period that indicates the file type by convention.
| Extension | Typical meaning |
|---|---|
.txt | Plain text |
.csv | Comma-separated values (tabular data) |
.py | Python source code |
.html | Web page markup |
.json | Structured data in JSON format |
.jpg | Compressed image |
.mp3 | Compressed audio |
.pdf | Portable document format |
Extensions are conventions, not enforced rules. A file named data.txt that actually contains an image will still look like a text file to programs trying to read it — and will likely cause an error. The extension describes what you intend the file to contain; it does not guarantee it.
The simplest kind of file is a plain text file. A plain text file stores a sequence of characters using a standard encoding such as UTF-8 (introduced in Chapter 4). There is no special formatting, no fonts, no embedded images — only characters.
Plain text files are used constantly in computing: source code in nearly every programming language, configuration files for servers and tools, log files recording what programs did and when, data files shared between programs, and documentation files like this one. The power of plain text is its simplicity. Any program that can read characters can read a plain text file. Plain text does not depend on any proprietary format or special software.
2024-09-01 08:14:22 User login: asmith 2024-09-01 08:15:03 File opened: report.csv 2024-09-01 08:16:45 File saved: report.csv 2024-09-01 08:22:11 User logout: asmith
username = student1 language = English theme = dark font_size = 14
# Reading: basic pattern file = open("notes.txt", "r") # "r" means open for reading contents = file.read() # read everything into a string file.close() # close the file when done print(contents) # Safer pattern using 'with' (auto-closes on exit) with open("notes.txt", "r") as file: contents = file.read() print(contents) # Writing plain text with open("output.txt", "w") as file: # "w" means open for writing file.write("Hello, file system.\n") file.write("This is a second line.\n")
Opening a file with "w" erases its previous contents immediately. To add to an existing file without erasing it, use "a" (append mode) instead. Always double-check the mode before writing to important files.
Much of the data programs work with is tabular — organized into rows and columns, like a spreadsheet or a database table. Consider a simple table of student records:
| student_id | name | grade | score |
|---|---|---|---|
| 1001 | Amara Osei | 10 | 87 |
| 1002 | Jin Park | 11 | 93 |
| 1003 | Fatima Nkrumah | 10 | 78 |
| 1004 | Liam Torres | 12 | 95 |
| 1005 | Sofia Reyes | 11 | 82 |
Columns (also called fields or attributes) represent categories of information. Each column has a name and holds one type of data: student_id, name, grade, score.
Rows (also called records) represent individual items — here, individual students. Each row contains one value per column.
Fields are the individual cells at the intersection of a specific row and column. The field at row 1002, column score, contains the value 93.
Headers are the labels in the first row that name the columns. Programs rely on headers to interpret data correctly.
This structure is fundamental. It appears in spreadsheets, database tables, CSV files, exported reports, data science datasets, and API responses. Understanding it is the starting point for nearly all data work in computing.
CSV stands for Comma-Separated Values. A CSV file is a plain text file where tabular data is represented by separating column values with commas and separating rows with newline characters. The student table above, stored as a CSV file, would look like this:
student_id,name,grade,score ← header row (column names) 1001,Amara Osei,10,87 1002,Jin Park,11,93 1003,Fatima Nkrumah,10,78 1004,Liam Torres,12,95 1005,Sofia Reyes,11,82
The first line is the header row — the column names. Each following line is one data row — one record. CSV is one of the most common data formats in computing because it is simple (just characters, commas, and newlines), universal (readable by spreadsheets, databases, programming languages, and text editors), and compact (no formatting overhead).
Real CSV files are rarely as clean as the example above. Three common complications arise.
Values containing commas → wrap the field in quotation marks: 1006,"Torres, Maria",12,90 Values containing quotation marks → double the quotes inside: 1007,Kai ""The Coder"" Kim,11,88 Missing values → comma is present but nothing appears between: 1008,,10,75 ← the name field is empty
Python has a built-in csv module that handles these complications automatically. DictReader reads each row as a dictionary, where keys are column names from the header row.
import csv with open("students.csv", "r") as file: reader = csv.DictReader(file) # reads header row automatically for row in reader: print(row["name"], row["score"])
Amara Osei 87
Jin Park 93
Fatima Nkrumah 78
Liam Torres 95
Sofia Reyes 82
Accessing fields by name (row["score"]) is clearer and less error-prone than accessing them by position (row[3]). If columns are ever reordered, named access still works; positional access breaks silently.
Programs that work with data typically follow a pattern called read-process-write, sometimes called ETL (Extract, Transform, Load). The core idea is simple but fundamental: read data from a source, process it (clean, filter, transform, or calculate), then write results to a destination.
┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ │ │ │ │ │ │ SOURCE │────▶│ PROCESS │────▶│ DESTINATION │ │ │ │ │ │ │ │ students.csv │ │ clean │ │ report.csv │ │ database │ │ filter │ │ screen output │ │ network API │ │ sort │ │ database │ │ user input │ │ summarize │ │ email / log │ │ │ │ transform │ │ │ └───────────────┘ └───────────────┘ └───────────────┘
This pattern appears in data science pipelines, database systems, web servers, financial systems, scientific computing, and everyday programs. Understanding it helps you read any data program, not just ones you write yourself.
For large files, reading the entire file at once can waste memory. Reading line by line processes one row at a time.
with open("students.csv", "r") as file: for line in file: print(line.strip()) # strip() removes the trailing newline character
This program reads student scores, adds 5 bonus points to each, and writes the updated records to a new file.
import csv # Step 1: Read students = [] with open("students.csv", "r") as file: reader = csv.DictReader(file) for row in reader: students.append(row) # Step 2: Process for student in students: original = int(student["score"]) student["score"] = original + 5 # Step 3: Write with open("students_updated.csv", "w", newline="") as file: writer = csv.DictWriter(file, fieldnames=["student_id", "name", "grade", "score"]) writer.writeheader() writer.writerows(students) print("Done. Updated scores written to students_updated.csv")
Notice the three-stage structure: open for reading → loop through rows → modify records → open for writing → write results. This pattern scales from tiny programs to industrial data pipelines processing millions of records per day.
In an ideal world, every dataset would arrive perfectly organized, complete, and correctly formatted. In practice, real data is frequently messy. Data cleaning (also called data wrangling) is the process of identifying and fixing problems in a dataset before analysis or use. It is often the most time-consuming step in any data project.
| Problem Type | Example | Risk if Ignored |
|---|---|---|
| Missing values | 1002,,11,93 — name is empty | Skewed calculations; program crashes |
| Format mismatch | 9/1/24 vs 2024-09-01 | Data misread or rejected |
| Duplicate records | Same row appearing twice | Inflated counts and sums |
| Out-of-range values | Score = 150 (max is 100) | Incorrect analysis; wrong conclusions |
A missing value occurs when a field contains no data for a particular record. Programs that encounter missing values must decide: skip the record, substitute a default value, flag it for human review, or halt with an error. The right choice depends on the context and the purpose of the data.
The same information might appear in different formats across records. Dates are a classic example: 2024-09-01, Sep 1, 2024, 9/1/24, and 01-09-2024 all represent the same date, but a program expecting one format will misread or reject the others. Other inconsistencies include capitalization, units, and abbreviations.
import csv cleaned = [] with open("students_raw.csv", "r") as file: reader = csv.DictReader(file) for row in reader: # Skip rows with missing names or scores if not row["name"] or not row["score"]: continue # Convert score to integer; skip if it fails try: score = int(row["score"]) except ValueError: continue # Skip out-of-range scores if score < 0 or score > 100: continue # Standardize name capitalization row["name"] = row["name"].strip().title() cleaned.append(row) print(f"Cleaned: {len(cleaned)} valid records")
Incorrect data produces incorrect conclusions no matter how sophisticated the analysis. A data scientist who skips cleaning and jumps straight to analysis is building conclusions on an unreliable foundation. Cleaning is unglamorous but essential.
Once data is clean, programs often need to filter it (keep only the records that meet a condition) or sort it (arrange records in a specific order). These two operations appear in almost every data program.
Filtering means selecting records that satisfy a condition and discarding the rest. Filters can test a single field or combine multiple conditions.
# Filter: grade 10 students only grade_10 = [] for student in students: if int(student["grade"]) == 10: grade_10.append(student) # Filter: combined condition — grade 10 AND score above 80 for student in students: if int(student["grade"]) == 10 and int(student["score"]) > 80: print(student["name"])
Sorting arranges records in ascending (low to high) or descending (high to low) order based on one or more fields. Python's sorted() function accepts a key argument that specifies how to compare records.
# Sort by score, highest first sorted_students = sorted(students, key=lambda s: int(s["score"]), reverse=True) for student in sorted_students: print(student["name"], student["score"]) # Multi-level sort: grade ascending, then score descending within each grade sorted_students = sorted( students, key=lambda s: (int(s["grade"]), -int(s["score"])) )
Liam Torres 95
Jin Park 93
Amara Osei 87
Sofia Reyes 82
Fatima Nkrumah 78
Real programs usually combine filtering and sorting in sequence.
# Grade 11 students, sorted by score descending grade_11_top = sorted( [s for s in students if int(s["grade"]) == 11], key=lambda s: int(s["score"]), reverse=True )
In a multi-level sort, the negative sign on a numeric field reverses the order for that field only. -int(s["score"]) sorts score from highest to lowest while leaving other fields in their default direction. This is a common Python idiom worth recognizing.
Beyond reading and filtering, programs often need to summarize data — computing statistics that describe the dataset as a whole rather than listing every individual record. Summaries appear in business reports, medical studies, school analytics, government statistics, and scientific research every day.
# Counting total = len(students) grade_10_count = sum(1 for s in students if int(s["grade"]) == 10) # Sum and average scores = [int(s["score"]) for s in students] average_score = sum(scores) / len(scores) print(f"Average: {average_score:.1f}") # .1f = one decimal place # Min and max print(f"Lowest: {min(scores)}") print(f"Highest: {max(scores)}")
Grouping means computing summaries for each category separately — for example, the average score per grade level.
from collections import defaultdict grade_scores = defaultdict(list) # each grade maps to a list of scores for student in students: grade = student["grade"] score = int(student["score"]) grade_scores[grade].append(score) for grade in sorted(grade_scores.keys()): scores = grade_scores[grade] avg = sum(scores) / len(scores) print(f"Grade {grade}: {len(scores)} students, average {avg:.1f}")
Grade 10: 2 students, average 82.5
Grade 11: 2 students, average 87.5
Grade 12: 1 students, average 95.0
Data about people is not abstract. Behind every row in a student table is a real student. Behind every health record is a real patient. Behind every financial transaction is a real account holder. The people whose information appears in data files have interests, rights, and expectations that computing professionals are obligated to respect.
Some data is more sensitive than other data. Categories that require particular care include:
| Category | Examples |
|---|---|
| PII — Personally identifiable information | Names, addresses, phone numbers, email, government IDs, birth dates |
| Health and medical | Diagnoses, medications, mental health records, genetic data |
| Financial | Income, credit history, debt, account numbers |
| Location | Home address, travel patterns, frequent locations |
| Behavioral | Purchases, searches, browsing history, communications |
Even data that seems harmless in isolation can become sensitive when combined. A dataset with no names but including home zip code, birth date, and gender can often be used to identify specific individuals — a problem called re-identification. Removing names from a dataset does not automatically make it anonymous.
Whoever holds data has power. A hospital holds data about patients' health. A school holds data about students' performance. A company holds data about customers' purchases and preferences. A government holds data about citizens' identities and behavior. This power creates responsibilities. When data is lost, stolen, sold without consent, or used in ways people did not agree to, real harm follows — people lose privacy, suffer discrimination, become targets of fraud, or find their histories used against them.
Computing professionals who handle data about real people operate in a field with genuine ethical stakes. The programs they write and the decisions they make have consequences for the people whose data they manage.
Before building a program that collects or processes data about people, thoughtful developers ask:
□ What data do we actually need? □ Where will it be stored, and who can access it? □ How long will we keep it, and when will we delete it? □ Are we collecting this data with users' knowledge and consent? □ What could go wrong if this data were accessed by the wrong person? □ Who is accountable if something goes wrong?
These questions are not only ethical concerns — they are increasingly legal requirements. Privacy laws in many regions create specific obligations for how personal data must be collected, stored, used, and protected. Ignorance of these obligations does not eliminate liability.
.. symbol moves up one level. File extensions indicate type by convention."w" erases existing content; use "a" to append.csv.DictReader automates parsing./ (root)
├── home/
│ └── student/
│ ├── data/
│ │ ├── students.csv ← /home/student/data/students.csv (absolute)
│ │ └── scores.csv
│ └── programs/
│ └── process.py ← /home/student/programs/process.py (absolute)
└── etc/
└── config.txt
From programs/, the relative path to students.csv is: ../data/students.csv
( .. moves up to student/ → then data/ → then students.csv )
student_id , name , grade , score ← header row (column names) ----------- ----------------- ----- ----- 1001 , Amara Osei , 10 , 87 ← row 1 (record 1) 1002 , Jin Park , 11 , 93 ← row 2 (record 2) 1003 , Fatima Nkrumah , 10 , 78 ← row 3 (record 3) ↑ ↑ ↑ ↑ field 1 field 2 field 3 field 4 Each cell (row ∩ column) is one field value. The field at row 1002, column "score" = 93.
students = [
{"name": "Mia", "grade": "10", "score": "72"},
{"name": "Leo", "grade": "11", "score": "88"},
{"name": "Zara", "grade": "10", "score": "91"},
]
for s in students:
if int(s["grade"]) == 10:
print(s["name"], int(s["score"]) + 5)
sorted_students = sorted(students, key=lambda s: int(s["score"])) for s in sorted_students: print(s["name"], s["score"])
strip() do, and why is it useful when reading lines from a file?with open("notes.txt", "r") as file: for line in file: print(line.strip())
name,score Alice,85 Bob,92 Carol,78
import csv scores = [] with open("scores.csv", "r") as file: reader = csv.DictReader(file) for row in reader: scores.append(int(row["score"])) print(min(scores)) print(max(scores)) print(sum(scores) / len(scores))
filtered = []
for s in students:
if int(s["grade"]) == 11 and int(s["score"]) > 85:
filtered.append(s)
filtered_sorted = sorted(filtered, key=lambda s: s["name"])
for s in filtered_sorted:
print(s["name"])
scores = [87, 93, 78, 95, 82, 65, 55, 91, 74, 88]
counts = {"A": 0, "B": 0, "C": 0, "D": 0, "F": 0}
for score in scores:
if score >= 90: counts["A"] += 1
elif score >= 80: counts["B"] += 1
elif score >= 70: counts["C"] += 1
elif score >= 60: counts["D"] += 1
else: counts["F"] += 1
for grade, count in counts.items():
print(f"{grade}: {count}")
.. mean in a relative file path? Give an example.int(row["score"]) do, and why might it raise an error?Identify each as an absolute path or a relative path. Explain how you can tell.
/home/student/data/grades.csvdata/grades.csv../reports/summary.txtC:\Users\admin\desktop\notes.txtRead this CSV data and answer the questions.
employee_id,department,salary,years E001,Engineering,72000,3 E002,Marketing,58000,7 E003,Engineering,81000,5 E004,HR,54000,2 E005,Marketing,61000,4 E006,Engineering,94000,9
Describe in plain English what each condition selects from a student dataset.
int(s["score"]) >= 90s["grade"] == "12"int(s["score"]) > 70 and int(s["score"]) < 85s["name"].startswith("A")Answer both parts without using a computer.
Torres, Osei, Nkrumah, Park, Reyes78, 95, 87, 82, 93Use these scores. Show your arithmetic.
Scores: 91, 76, 84, 63, 88, 72, 95, 80, 68, 77
Identify every data quality problem in this CSV. For each, name the type and describe the specific issue.
student_id,name,grade,score 1001,Marcus Chen,10,87 1002,,11,93 1003,Layla Hassan,10,78 1003,Layla Hassan,10,78 1004,Raj Patel,12,115 1005,amara osei,11,82 1006,Sofia Torres,9/1/2008,74
A school stores a CSV file containing student names, home addresses, parent contact information, health conditions, and academic records.
What causes this program to crash? Identify two separate scenarios and describe how to fix each. Hint: what happens if the file is empty? What if a score field contains a non-numeric value?
There are grade 10 students in the list, but nothing prints. Find the bug. Explain why it causes silence rather than an error message.
The output file exists but contains no readable data. What single line is missing? Where should it be inserted, and why is it needed?
This path is supposed to point to a file two levels above the current folder, inside a folder called archive. What is wrong with it? Write the correct path.
Design a CSV table to store information about library books. The table must be able to answer these questions: Who is the author? What is the title? Is the book currently checked out? When is it due back? How many times has it been borrowed this year?
A program will receive a CSV file of product inventory data from an external supplier. The file contains product names, prices, quantities, and category codes.
Describe in plain English — not code — a complete read-process-write pipeline that reads a CSV file of employee records (name, department, salary, years of service), removes records with missing values, filters for employees with more than 3 years of service, calculates the average salary for each department, and writes a summary CSV with one row per department.
Web servers generate log files recording every request they receive. A typical log line:
2024-09-15 14:22:03 192.168.1.45 GET /index.html 200 1543
This records: date and time · visitor IP address · request type · page requested · status code (200 = success) · bytes transferred.
for loop or a while loop? Explain your choice.score > 90. Would this be faster if the records were pre-sorted by score? Explain using the concept of binary search.report.csv:/ → home/ → user/ → work/ → report.csv
/home/user/work/. Write a relative path to reach /home/user/data/scores.csv..csv indicate? Is this extension a guarantee about the file's contents? Explain.id,city,population
1,Lagos,15000000
2,Cairo,10000000
Torres, Maria in quotation marks when writing it to a file?data = [{"city":"A","pop":"50000"},{"city":"B","pop":"120000"},{"city":"C","pop":"30000"}]
result = sorted(data, key=lambda d: int(d["pop"]), reverse=True)
for r in result: print(r["city"])
Chapter 14 focused on how programs handle data — reading, writing, cleaning, filtering, sorting, and summarizing files. All of this work happens on physical hardware: circuits, chips, drives, and connectors that actually store and move the bits. Chapter 15 introduces the physical components of computing systems — the CPU, memory, storage, input and output devices, buses, and the fetch-decode-execute cycle. Understanding hardware explains why programs behave the way they do: why some operations are fast and others slow, why memory fills up, why storage and memory are different kinds of resources, and why the physical world eventually constrains every software system.