### π₯ Pickling, UnPickling & Structured Data Storage!
> You've mastered text files β now level up! **Binary files** let you store *any Python object* directly (lists, dicts, class objects!) and **CSV files** let you store *structured tabular data* that opens straight in Excel! π
---
::: grid
::: card 5.5.1 | Binary File Basics | Creating, opening and closing binary files with the right modes | wb, rb, ab, rb+
::: card 5.5.2 | Pickling | Serialize any Python object and write it to a binary file | pickle.dump()
::: card 5.5.3 | UnPickling | Deserialize and restore Python objects back from binary files | pickle.load()
::: card 5.5.4 | Searching | Find specific records by looping through a binary file | EOFError, loop
::: card 5.5.5 | Updating | Modify records using the Read-All β Modify β Write-All pattern | update, rewrite
::: card 5.6 | CSV Files | Read and write structured comma-separated tabular data | csv module, reader, writer
:::
---
## π 5.5 Working with Binary Files
### What Makes Binary Files Special?
Text files are great for storing simple strings β but what if you want to save a **complete Python dictionary**, a **list of student records**, or even a **custom class object** directly to disk? That's where binary files shine!
Binary files store data in the **same format as Python's internal memory (RAM) representation** β raw bytes. This means:
- β **Any Python object** can be stored β lists, dicts, tuples, class instances
- β **No data type conversion** needed (text files force everything into strings)
- β **Faster** for complex, structured records
- β **Not human-readable** β you need Python to open and decode them
> [!NOTE]
> **Memory Trick:** Text files are like writing a letter in English β anyone can read it. Binary files are like a **WhatsApp voice note stored as bytes** β only the app (Python) can decode and play it back! π€
---
### π The `pickle` Module β Python's Magic Serializer!
Python handles all binary file serialization through the built-in **`pickle` module**. It converts Python objects into a byte stream (and reconstructs them back perfectly).
> [!RULE]
> **Two core pickle functions:**
> - `pickle.dump(object, file)` β **Serialize** and **write** object to binary file
> - `pickle.load(file)` β **Read** from binary file and **reconstruct** back to Python object
>
> The conversion Python object β bytes is called **Pickling** (Serialization).
> The reverse, bytes β Python object, is called **UnPickling** (Deserialization).
---
### πΊοΈ Pickling & UnPickling β The Full Picture
π₯ Pickling β Writing to Binary File
```mermaid
flowchart LR
A["π Python Object\nlist / dict / object"]
B["open() with 'wb'\nOpen file for writing"]
C["pickle.dump()\nSerialize to bytes"]
D["πΎ Binary File\nstudents.dat"]
A --> B --> C --> D
classDef tryNode fill:none;
classDef elseNode fill:none;
classDef normalNode fill:none;
class A elseNode;
class B,C tryNode;
class D normalNode;
```
π¦ Unpickling β Reading from Binary File
```mermaid
flowchart LR
D["πΎ Binary File\nstudents.dat"]
E["open() with 'rb'\nOpen file for reading"]
F["pickle.load()\nDeserialize bytes"]
G["π Python Object\nRestored perfectly!"]
D --> E --> F --> G
classDef tryNode fill:none;
classDef elseNode fill:none;
classDef normalNode fill:none;
class D normalNode;
class E,F tryNode;
class G elseNode;
```
---
### π 5.5.1 Creating / Opening / Closing Binary Files
Binary files use the **same `open()` function** as text files β just add **`b`** to the mode string!
- **`'wb'` β Write Binary π¦**
* Opens for **writing in binary mode**
* Creates new file if it doesn't exist
* **Overwrites and destroys** existing content (like `'w'` in text mode) β οΈ
* Use for: Creating a fresh binary file from scratch
- **`'rb'` β Read Binary π**
* Opens for **reading in binary mode** β file must exist!
* Raises `FileNotFoundError` if file is missing
* Use for: Reading, displaying, or searching records
- **`'ab'` β Append Binary β**
* Opens for **appending in binary mode**
* Adds new data at the very end; **preserves all existing records**
* Use for: Adding new records without touching old ones
- **`'rb+'` β Read + Write Binary π**
* Opens for **both reading and writing** in binary mode
* File **must already exist** β does NOT create a new file
* Use for: Complex operations needing both read and write
> [!IMPORTANT]
> **Board Exam Hotspot:** Binary files in CBSE exams almost always use the `.dat` extension (e.g., `students.dat`). Python doesn't enforce this β it's just convention. Always `import pickle` at the top of any binary file program!
```python
import pickle
# The standard pattern for binary file operations
f = open("students.dat", "wb") # Open in binary write mode
# ... operations ...
f.close() # Always close!
```
---
### π₯ 5.5.2 Writing onto a Binary File β Pickling!
To write a Python object into a binary file, use **`pickle.dump()`**. You can dump dictionaries, lists, tuples, strings, numbers β literally any Python object.
> [!RULE]
> **Syntax:**
> ```
> pickle.dump(object, file_object)
> ```
> - `object` β The Python object to serialize and store
> - `file_object` β File opened in `'wb'` (new) or `'ab'` (append) mode
```python
import pickle
# Writing a single dictionary record
f = open("students.dat", "wb")
student = {"name": "Aman Sharma", "roll": 1, "marks": 95}
pickle.dump(student, f)
f.close()
print("Record written! β ")
```
```python
import pickle
# Writing MULTIPLE records β one dump per record!
students = [
{"roll": 1, "name": "Aman Sharma", "marks": 95},
{"roll": 2, "name": "Priya Singh", "marks": 88},
{"roll": 3, "name": "Rohan Gupta", "marks": 76},
{"roll": 4, "name": "Sneha Joshi", "marks": 92},
]
f = open("students.dat", "wb")
for student in students:
pickle.dump(student, f) # Each dict dumped separately!
f.close()
print(f"{len(students)} records written! β ")
```
```python
import pickle
# Appending a NEW record without losing existing data
f = open("students.dat", "ab") # 'ab' = safe append
new_student = {"roll": 5, "name": "Kavya Reddy", "marks": 98}
pickle.dump(new_student, f)
f.close()
print("New student appended! β ")
```
> [!WARNING]
> **Common Mistake:** When writing multiple records with `pickle.dump()` in a loop, each call writes **one independent object**. Don't dump a single giant list if you plan to update individual records later β dump them **one by one** so you can read them back one by one with `pickle.load()`. π¨
> [!TIP]
> **Study Strategy:** Think of `pickle.dump()` like putting items into **individual sealed courier parcels** on a conveyor belt. Each object gets its own package. `pickle.load()` unpacks them one at a time from the other end! π¦
---
### π¦ 5.5.3 Reading from a Binary File β UnPickling!
To retrieve data from a binary file, use **`pickle.load()`**. Each call reads exactly **one object** that was previously dumped β in the same order they were written.
> [!RULE]
> **Syntax:**
> ```
> object = pickle.load(file_object)
> ```
> - `file_object` β File opened in `'rb'` mode
> - Returns the **next reconstructed Python object** from the file
> - Raises **`EOFError`** when no more objects remain (end of file)
```python
import pickle
# Reading a single record
f = open("students.dat", "rb")
student = pickle.load(f)
print(student)
# {'roll': 1, 'name': 'Aman Sharma', 'marks': 95}
f.close()
```
```python
import pickle
# π Reading ALL records β The Standard CBSE Pattern
f = open("students.dat", "rb")
print("All Student Records:")
print("-" * 40)
while True:
try:
student = pickle.load(f)
print(f"Roll: {student['roll']} | Name: {student['name']} | Marks: {student['marks']}")
except EOFError:
break # No more records β exit the loop cleanly
f.close()
```
> [!IMPORTANT]
> **Board Exam Hotspot:** The **`EOFError`** (End of File Error) is how Python signals that all objects have been read. The pattern below is **essential to memorize** β it appears in virtually every binary file reading/searching question:
> ```python
> while True:
> try:
> record = pickle.load(f)
> # process record
> except EOFError:
> break
> ```
> [!NOTE]
> **Memory Trick:**
> - `dump()` β **D**rop object **U**nder disk's **M**emory **P**rotection (write!)
> - `load()` β **L**ift **O**bject **A**nd **D**eliver back (read!)
>
> Together they're like a **courier service** β `dump()` ships the parcel, `load()` delivers it back to you! π
---
### π 5.5.4 Searching in a Binary File
Searching a binary file means reading records **one by one** and checking each against your criteria. There's no shortcut β you must **read β compare β act** for every record.
```mermaid
flowchart TD
A["Open file\nin 'rb' mode"]
B["pickle.load()\nRead one record"]
C{"Record matches\nthe search criteria?"}
D["β Found!\nDisplay / Process it"]
E{"EOFError\nraised?"}
F["β Record not found\nPrint message"]
G["Close file"]
A --> B
B --> C
C -->|"Yes β Match!"| D
C -->|"No β Keep looking"| E
D --> G
E -->|"No β more records"| B
E -->|"Yes β file ended"| F
F --> G
classDef normalNode fill:none;
classDef elseNode fill:none;
classDef exceptNode fill:none;
classDef tryNode fill:none;
class A normalNode;
class B tryNode;
class C normalNode;
class D elseNode;
class E normalNode;
class F exceptNode;
class G normalNode;
```
```python
import pickle
# Search for a student by roll number
def search_student(roll_no):
found = False
try:
f = open("students.dat", "rb")
while True:
try:
student = pickle.load(f)
if student["roll"] == roll_no:
print("\nβ Student Found!")
print(f" Name : {student['name']}")
print(f" Roll : {student['roll']}")
print(f" Marks : {student['marks']}")
found = True
break
except EOFError:
break
f.close()
except FileNotFoundError:
print("Error: File not found!")
if not found:
print(f"β No student with roll {roll_no} found.")
search_student(3)
```
```python
import pickle
# Search ALL students scoring above a cutoff (multiple matches)
def toppers(cutoff):
print(f"\nπ Students with marks > {cutoff}:")
print("-" * 35)
count = 0
f = open("students.dat", "rb")
while True:
try:
s = pickle.load(f)
if s["marks"] > cutoff:
print(f" {s['name']} β {s['marks']}")
count += 1
except EOFError:
break
f.close()
print(f"\nTotal: {count} student(s)")
toppers(85)
```
> [!TIP]
> **Study Strategy:** Every binary file search follows the **same 3-step skeleton** β master this and no question can beat you:
> 1. Open file in `'rb'` mode
> 2. `while True` β `try: pickle.load()` β check condition β `except EOFError: break`
> 3. Close file
>
> The only thing that changes between questions is the **condition inside step 2**! π―
---
### βοΈ 5.5.5 Updating in a Binary File
Unlike text files, **you cannot edit a single record inside a pickle binary file directly** β changing one record can shift byte positions and corrupt everything after it. The safe and standard CBSE approach is the **Read-All β Modify β Write-All-Back** pattern:
```mermaid
flowchart LR
A["Step 1οΈβ£\nOpen 'rb'\nRead ALL records into a list"]
B["Step 2οΈβ£\nFind & modify\nthe target record in the list"]
C["Step 3οΈβ£\nOpen 'wb'\nOverwrite file with modified list"]
D["β File Updated!"]
A --> B
B --> C
C --> D
classDef tryNode fill:none;
classDef elseNode fill:none;
classDef normalNode fill:none;
class A tryNode;
class B elseNode;
class C tryNode;
class D normalNode;
```
```python
import pickle
def update_marks(roll_no, new_marks):
# ββ Step 1: Read ALL records into a Python list ββ
records = []
try:
f = open("students.dat", "rb")
while True:
try:
records.append(pickle.load(f))
except EOFError:
break
f.close()
except FileNotFoundError:
print("Error: File not found!")
return
# ββ Step 2: Find and modify the target record ββββ
updated = False
for record in records:
if record["roll"] == roll_no:
print(f"Old marks for {record['name']}: {record['marks']}")
record["marks"] = new_marks
print(f"Updated marks: {record['marks']}")
updated = True
break
if not updated:
print(f"β Roll {roll_no} not found!")
return
# ββ Step 3: Write ALL records back to file βββββββ
f = open("students.dat", "wb") # 'wb' overwrites completely
for record in records:
pickle.dump(record, f)
f.close()
print("β Record updated successfully!")
update_marks(2, 95)
```
```python
import pickle
# Deleting a record β same Read-Modify-Write pattern!
def delete_student(roll_no):
records = []
f = open("students.dat", "rb")
while True:
try:
rec = pickle.load(f)
if rec["roll"] != roll_no: # Keep ALL except the target
records.append(rec)
except EOFError:
break
f.close()
f = open("students.dat", "wb") # Rewrite without deleted record
for rec in records:
pickle.dump(rec, f)
f.close()
print(f"β Student with roll {roll_no} deleted!")
delete_student(3)
```
> [!IMPORTANT]
> **Board Exam Hotspot:** The update/delete pattern is a **guaranteed exam question**. Always follow:
> 1. `'rb'` β Read all β store in list
> 2. Modify the list in memory
> 3. `'wb'` β Write entire modified list back
>
> Never attempt to overwrite a single record directly inside a pickle file! π¨
> [!WARNING]
> **Common Mistake:** Students try to use `'rb+'` mode thinking they can edit a specific record in place. **Don't!** Pickle records vary in size β overwriting one record shifts all byte positions and corrupts everything after it. Always use the read-all β modify β write-all approach! π₯
---
### π§© Complete Binary File Program β All Operations
```python
import pickle
FILENAME = "school.dat"
def create_records():
students = [
{"roll": 1, "name": "Aman Sharma", "marks": 95, "grade": "A+"},
{"roll": 2, "name": "Priya Singh", "marks": 88, "grade": "A"},
{"roll": 3, "name": "Rohan Gupta", "marks": 76, "grade": "B+"},
{"roll": 4, "name": "Sneha Joshi", "marks": 92, "grade": "A+"},
{"roll": 5, "name": "Kavya Reddy", "marks": 65, "grade": "B"},
]
f = open(FILENAME, "wb")
for s in students:
pickle.dump(s, f)
f.close()
print("β Records created!")
def display_all():
f = open(FILENAME, "rb")
print(f"\n{'Roll':<6} {'Name':<20} {'Marks':<8} {'Grade'}")
print("-" * 42)
while True:
try:
s = pickle.load(f)
print(f"{s['roll']:<6} {s['name']:<20} {s['marks']:<8} {s['grade']}")
except EOFError:
break
f.close()
def search_by_name(keyword):
f = open(FILENAME, "rb")
print(f"\nπ Searching for '{keyword}':")
found = False
while True:
try:
s = pickle.load(f)
if keyword.lower() in s["name"].lower():
print(f" β Roll {s['roll']}: {s['name']} | Marks: {s['marks']}")
found = True
except EOFError:
break
f.close()
if not found:
print(" No match found!")
def update_grade(roll, new_grade):
records = []
f = open(FILENAME, "rb")
while True:
try:
records.append(pickle.load(f))
except EOFError:
break
f.close()
for r in records:
if r["roll"] == roll:
r["grade"] = new_grade
f = open(FILENAME, "wb")
for r in records:
pickle.dump(r, f)
f.close()
print(f"β Grade updated for roll {roll}")
# Run all operations
create_records()
display_all()
search_by_name("Singh")
update_grade(3, "A")
display_all()
```
---
## π 5.6 Working with CSV Files
### What is a CSV File?
**CSV** stands for **Comma-Separated Values**. It's a universal, plain-text format for storing **structured tabular data** β just like an Excel spreadsheet, but in a simple `.csv` file.
Imagine this Excel table:
| Name | Roll | Marks | Grade |
|------|------|-------|-------|
| Aman | 1 | 95 | A+ |
| Priya | 2 | 88 | A |
In CSV format, that exact same data becomes:
```
Name,Roll,Marks,Grade
Aman,1,95,A+
Priya,2,88,A
```
That's it! Just values separated by commas, one row per line. Beautifully simple. π
> [!NOTE]
> **Memory Trick:** CSV = **C**ommas **S**eparating **V**alues! Every row is one line, every column is a comma-separated value. The simplest possible table format β yet accepted by Excel, Google Sheets, MySQL, PostgreSQL, and practically every tool on Earth! π
- **Why CSV over plain text files? π**
* **Structure** β Data organized in rows and columns β not just random lines
* **Universal** β Every tool understands CSV: Excel, databases, Python, R, Java...
* **Human-readable** β Unlike binary, you can open and read it in Notepad
* **Lightweight** β No database software needed β just a `.csv` file!
* **Perfect for** β Student records, marks sheets, sales data, inventory, logs
- **Python's `csv` Module π**
* Built-in module β no installation needed (`import csv`)
* Handles commas **inside values** automatically using quoting
* `csv.writer` / `csv.reader` β list-based read/write
* `csv.DictWriter` / `csv.DictReader` β dictionary-based read/write (cleaner!)
> [!RULE]
> **Always import csv at the top:**
> ```python
> import csv
> ```
---
### π 5.6.1 Opening / Closing CSV Files
CSV files are opened with the same `open()` function β with one critical extra argument:
> [!RULE]
> **The Standard CSV Opening Pattern:**
> ```python
> f = open("data.csv", "w", newline='')
> ```
> The **`newline=''`** parameter is essential β it prevents Python from adding extra blank lines between rows (especially on Windows)!
- **Modes for CSV files π**
* `'w'` β Write (creates new / overwrites existing)
* `'r'` β Read (file must exist)
* `'a'` β Append (adds to end, preserves existing data)
```python
import csv
# Writing β always newline=''
f = open("marks.csv", "w", newline='')
# ... write operations ...
f.close()
# Reading β good practice to include newline='' here too
f = open("marks.csv", "r", newline='')
# ... read operations ...
f.close()
# Best practice β with statement (auto-closes, cleaner!)
with open("marks.csv", "w", newline='') as f:
pass # operations here
```
> [!WARNING]
> **Common Mistake:** Forgetting `newline=''` when **writing** CSV on Windows inserts a **blank line between every data row**! This breaks the file structure and trips up every tool trying to read it. Always include `newline=''` β no exceptions! π¨
---
### βοΈ 5.6.2 Writing in CSV Files
Two approaches β choose based on whether you're working with lists or dictionaries:
- **βοΈ `csv.writer` + `writerow()` / `writerows()`**
* Takes **lists or tuples** β each list is one row
* `writerow(list)` β Write a **single row**
* `writerows(list_of_lists)` β Write **all rows** at once β efficient!
- **π `csv.DictWriter` + `writeheader()` + `writerow()`**
* Takes **dictionaries** β keys become column headers automatically
* `writeheader()` β Write the first row as column names
* `writerow(dict)` β Write one dict as one data row
#### Method 1 β `csv.writer` (List-based)
```python
import csv
# writerow() β one row at a time
with open("students.csv", "w", newline='') as f:
writer = csv.writer(f)
writer.writerow(["Roll", "Name", "Marks", "Grade"]) # Header
writer.writerow([1, "Aman Sharma", 95, "A+"])
writer.writerow([2, "Priya Singh", 88, "A"])
writer.writerow([3, "Rohan Gupta", 76, "B+"])
print("CSV written! β ")
# File (students.csv):
# Roll,Name,Marks,Grade
# 1,Aman Sharma,95,A+
# 2,Priya Singh,88,A
# 3,Rohan Gupta,76,B+
```
```python
import csv
# writerows() β write ALL rows in one efficient call!
with open("students.csv", "w", newline='') as f:
writer = csv.writer(f)
header = ["Roll", "Name", "Marks", "Grade"]
data = [
[1, "Aman Sharma", 95, "A+"],
[2, "Priya Singh", 88, "A"],
[3, "Rohan Gupta", 76, "B+"],
[4, "Sneha Joshi", 92, "A+"],
[5, "Kavya Reddy", 65, "B"],
]
writer.writerow(header) # Header first
writer.writerows(data) # All data in one go!
print("All records written with writerows()! β ")
```
#### Method 2 β `csv.DictWriter` (Dictionary-based)
```python
import csv
# DictWriter β cleaner, safer for complex data
with open("students.csv", "w", newline='') as f:
fields = ["Roll", "Name", "Marks", "Grade"]
writer = csv.DictWriter(f, fieldnames=fields)
writer.writeheader() # Writes: Roll,Name,Marks,Grade
students = [
{"Roll": 1, "Name": "Aman Sharma", "Marks": 95, "Grade": "A+"},
{"Roll": 2, "Name": "Priya Singh", "Marks": 88, "Grade": "A"},
{"Roll": 3, "Name": "Rohan Gupta", "Marks": 76, "Grade": "B+"},
]
for s in students:
writer.writerow(s)
print("DictWriter done! β ")
```
> [!IMPORTANT]
> **Board Exam Hotspot β `write` methods summary:**
> | Method | Used With | Writes |
> |--------|-----------|--------|
> | `writerow(list)` | `csv.writer` | One row as a list |
> | `writerows(list_of_lists)` | `csv.writer` | Multiple rows at once |
> | `writeheader()` | `csv.DictWriter` only | Column names as first row |
> | `writerow(dict)` | `csv.DictWriter` | One row as a dictionary |
---
### π 5.6.3 Reading in CSV Files
Reading mirrors writing β two clean approaches:
- π **`csv.reader`** β Reads each row as a **list of strings**
- π **`csv.DictReader`** β Reads each row as a **dictionary** (first row = keys)
#### Method 1 β `csv.reader` (List-based)
```python
import csv
# Basic reading β each row is a list
with open("students.csv", "r", newline='') as f:
reader = csv.reader(f)
for row in reader:
print(row)
# Output:
# ['Roll', 'Name', 'Marks', 'Grade'] β header row (a list!)
# ['1', 'Aman Sharma', '95', 'A+'] β all values are STRINGS!
# ['2', 'Priya Singh', '88', 'A']
```
```python
import csv
# Skip header, then process data rows
with open("students.csv", "r", newline='') as f:
reader = csv.reader(f)
header = next(reader) # Read and skip the header row
print(f"Columns: {header}")
for row in reader:
roll = int(row[0]) # β Convert string to int!
name = row[1]
marks = int(row[2]) # β CSV gives strings β always convert!
grade = row[3]
print(f"Roll {roll}: {name} scored {marks} marks ({grade})")
```
#### Method 2 β `csv.DictReader` (Dictionary-based β Recommended!)
```python
import csv
# DictReader β access columns by name, not index
with open("students.csv", "r", newline='') as f:
reader = csv.DictReader(f) # First row auto-becomes dict keys!
for row in reader:
# Access by column name β much cleaner than row[0], row[2]!
print(f"{row['Name']} β Marks: {row['Marks']}, Grade: {row['Grade']}")
```
> [!WARNING]
> **Common Mistake:** `csv.reader` reads **everything as strings** β even numbers! `95` becomes `'95'`. Always convert:
> ```python
> marks = int(row[2]) # β '95' β 95
> price = float(row[3]) # β '45.5' β 45.5
> ```
> Forgetting this breaks any arithmetic and causes `TypeError`! π¨
> [!NOTE]
> **Memory Trick:**
> - `csv.writer` / `csv.reader` β Work with **lists** (access by position: `row[0]`, `row[1]`)
> - `csv.DictWriter` / `csv.DictReader` β Work with **dicts** (access by name: `row['Name']`, `row['Marks']`)
>
> The **Dict** variants are cleaner, safer, and far more readable. Prefer them whenever possible! π
---
### βοΈ CSV Write Operation β Visual Guide
```mermaid
flowchart LR
A["π Python Data\nLists / Dicts"]
B["open() with\nnewline=''"]
C["csv.writer\nor DictWriter"]
D["writerow()\nwriterows()\nwriteheader()"]
E["πΎ CSV File\nstudents.csv"]
A --> B --> C --> D --> E
classDef dataNode fill:none;
classDef fileNode fill:none;
class A,E dataNode;
class B,C,D fileNode;
```
### π CSV Read Operation β Visual Guide
```mermaid
flowchart LR
E["πΎ CSV File\nstudents.csv"]
F["open() with\nnewline=''"]
G["csv.reader\nor DictReader"]
H["for row in reader\nProcess each row"]
I["β Restored Data\n(convert types!)"]
E --> F --> G --> H --> I
classDef dataNode fill:none;
classDef fileNode fill:none;
class E,I dataNode;
class F,G,H fileNode;
```
---
### π§© Complete CSV Program β All Operations
```python
import csv
FILENAME = "library.csv"
FIELDS = ["BookID", "Title", "Author", "Price", "Qty"]
def create_library():
"""Write initial records"""
books = [
[101, "Python Basics", "Guido Van", 450, 15],
[102, "Data Structures", "Mark Allen", 620, 8],
[103, "Computer Networks", "Forouzan", 780, 12],
[104, "DBMS Concepts", "Korth", 590, 5],
[105, "Artificial Intelligence","Russell", 850, 10],
]
with open(FILENAME, "w", newline='') as f:
writer = csv.writer(f)
writer.writerow(FIELDS) # Header
writer.writerows(books) # All data
print("β Library CSV created!")
def display_all():
"""Read and display all books"""
with open(FILENAME, "r", newline='') as f:
reader = csv.DictReader(f)
print(f"\n{'ID':<6} {'Title':<28} {'Author':<16} {'Price':>7} {'Qty':>5}")
print("-" * 65)
for row in reader:
print(f"{row['BookID']:<6} {row['Title']:<28} {row['Author']:<16} βΉ{row['Price']:>5} {row['Qty']:>5}")
def search_by_author(name):
"""Search books by author name"""
print(f"\nπ Books by '{name}':")
found = False
with open(FILENAME, "r", newline='') as f:
reader = csv.DictReader(f)
for row in reader:
if name.lower() in row["Author"].lower():
print(f" β {row['Title']} (βΉ{row['Price']})")
found = True
if not found:
print(" No books found!")
def add_book(bid, title, author, price, qty):
"""Append a new book"""
with open(FILENAME, "a", newline='') as f:
writer = csv.writer(f)
writer.writerow([bid, title, author, price, qty])
print(f"β '{title}' added to library!")
# Run all operations
create_library()
display_all()
search_by_author("Korth")
add_book(106, "Operating Systems", "Silberschatz", 710, 7)
display_all()
```
---
## π Binary File vs CSV File β Side-by-Side
> [!IMPORTANT]
> **Board Exam Hotspot β The Must-Know Comparison Table:**
>
> | Feature | Binary File (`pickle`) | CSV File (`csv`) |
> |---------|----------------------|-----------------|
> | Module | `pickle` | `csv` |
> | Human-readable? | β No | β Yes |
> | Stores Python objects? | β Any object | β οΈ Strings only |
> | Opens in Excel? | β No | β Yes |
> | File mode | `'rb'`, `'wb'`, `'ab'` | `'r'`, `'w'`, `'a'` |
> | Write function | `pickle.dump()` | `writerow()` / `writerows()` |
> | Read function | `pickle.load()` | `for row in reader:` |
> | End of file detection | `EOFError` exception | Loop ends naturally |
> | Type preservation | β int, list, dict intact | β Everything becomes string |
> | Special parameter | None | `newline=''` in `open()` |
---
## π Rapid Revision β Board Exam Ready!
> [!TIP]
> **Study Strategy β 6 Must-Practise CBSE Questions:**
> 1. ποΈ "What is pickling? Write a program to write and read a dictionary using pickle"
> 2. ποΈ "Write a program to search a record in a binary file by a given field"
> 3. ποΈ "Write a program to update/modify a record in a binary file"
> 4. ποΈ "Difference between text file, binary file, and CSV file"
> 5. ποΈ "Write a program to write data into a CSV file using csv.writer"
> 6. ποΈ "Why is `newline=''` used when opening a CSV file in Python?"
> [!WARNING]
> **Common Mistakes β Final Exam Checklist:**
> 1. β Forgetting `import pickle` or `import csv`
> 2. β Opening binary files in text mode (`'w'` instead of `'wb'`)
> 3. β Not handling `EOFError` in binary file reading loops
> 4. β Forgetting `newline=''` in CSV `open()` β causes blank rows
> 5. β Not converting CSV string values to `int`/`float` before arithmetic
> 6. β Trying to update a binary file record in-place (using `'rb+'`) instead of the read-all β modify β write-all pattern
```python
# βοΈ Bonus Program: Export Binary File β CSV File
# The ultimate combo question β often seen in CBSE practicals!
import pickle, csv
# Step 1: Read all records from binary file
records = []
with open("students.dat", "rb") as bf:
while True:
try:
records.append(pickle.load(bf))
except EOFError:
break
# Step 2: Write all records to CSV
with open("students.csv", "w", newline='') as cf:
if records:
writer = csv.DictWriter(cf, fieldnames=records[0].keys())
writer.writeheader()
writer.writerows(records)
print(f"β Exported {len(records)} records from binary (.dat) to CSV!")
```
> You've mastered text files β now level up! **Binary files** let you store *any Python object* directly (lists, dicts, class objects!) and **CSV files** let you store *structured tabular data* that opens straight in Excel! π
---
::: grid
::: card 5.5.1 | Binary File Basics | Creating, opening and closing binary files with the right modes | wb, rb, ab, rb+
::: card 5.5.2 | Pickling | Serialize any Python object and write it to a binary file | pickle.dump()
::: card 5.5.3 | UnPickling | Deserialize and restore Python objects back from binary files | pickle.load()
::: card 5.5.4 | Searching | Find specific records by looping through a binary file | EOFError, loop
::: card 5.5.5 | Updating | Modify records using the Read-All β Modify β Write-All pattern | update, rewrite
::: card 5.6 | CSV Files | Read and write structured comma-separated tabular data | csv module, reader, writer
:::
---
## π 5.5 Working with Binary Files
### What Makes Binary Files Special?
Text files are great for storing simple strings β but what if you want to save a **complete Python dictionary**, a **list of student records**, or even a **custom class object** directly to disk? That's where binary files shine!
Binary files store data in the **same format as Python's internal memory (RAM) representation** β raw bytes. This means:
- β **Any Python object** can be stored β lists, dicts, tuples, class instances
- β **No data type conversion** needed (text files force everything into strings)
- β **Faster** for complex, structured records
- β **Not human-readable** β you need Python to open and decode them
> [!NOTE]
> **Memory Trick:** Text files are like writing a letter in English β anyone can read it. Binary files are like a **WhatsApp voice note stored as bytes** β only the app (Python) can decode and play it back! π€
---
### π The `pickle` Module β Python's Magic Serializer!
Python handles all binary file serialization through the built-in **`pickle` module**. It converts Python objects into a byte stream (and reconstructs them back perfectly).
> [!RULE]
> **Two core pickle functions:**
> - `pickle.dump(object, file)` β **Serialize** and **write** object to binary file
> - `pickle.load(file)` β **Read** from binary file and **reconstruct** back to Python object
>
> The conversion Python object β bytes is called **Pickling** (Serialization).
> The reverse, bytes β Python object, is called **UnPickling** (Deserialization).
---
### πΊοΈ Pickling & UnPickling β The Full Picture
π₯ Pickling β Writing to Binary File
```mermaid
flowchart LR
A["π Python Object\nlist / dict / object"]
B["open() with 'wb'\nOpen file for writing"]
C["pickle.dump()\nSerialize to bytes"]
D["πΎ Binary File\nstudents.dat"]
A --> B --> C --> D
classDef tryNode fill:none;
classDef elseNode fill:none;
classDef normalNode fill:none;
class A elseNode;
class B,C tryNode;
class D normalNode;
```
π¦ Unpickling β Reading from Binary File
```mermaid
flowchart LR
D["πΎ Binary File\nstudents.dat"]
E["open() with 'rb'\nOpen file for reading"]
F["pickle.load()\nDeserialize bytes"]
G["π Python Object\nRestored perfectly!"]
D --> E --> F --> G
classDef tryNode fill:none;
classDef elseNode fill:none;
classDef normalNode fill:none;
class D normalNode;
class E,F tryNode;
class G elseNode;
```
---
### π 5.5.1 Creating / Opening / Closing Binary Files
Binary files use the **same `open()` function** as text files β just add **`b`** to the mode string!
- **`'wb'` β Write Binary π¦**
* Opens for **writing in binary mode**
* Creates new file if it doesn't exist
* **Overwrites and destroys** existing content (like `'w'` in text mode) β οΈ
* Use for: Creating a fresh binary file from scratch
- **`'rb'` β Read Binary π**
* Opens for **reading in binary mode** β file must exist!
* Raises `FileNotFoundError` if file is missing
* Use for: Reading, displaying, or searching records
- **`'ab'` β Append Binary β**
* Opens for **appending in binary mode**
* Adds new data at the very end; **preserves all existing records**
* Use for: Adding new records without touching old ones
- **`'rb+'` β Read + Write Binary π**
* Opens for **both reading and writing** in binary mode
* File **must already exist** β does NOT create a new file
* Use for: Complex operations needing both read and write
> [!IMPORTANT]
> **Board Exam Hotspot:** Binary files in CBSE exams almost always use the `.dat` extension (e.g., `students.dat`). Python doesn't enforce this β it's just convention. Always `import pickle` at the top of any binary file program!
```python
import pickle
# The standard pattern for binary file operations
f = open("students.dat", "wb") # Open in binary write mode
# ... operations ...
f.close() # Always close!
```
---
### π₯ 5.5.2 Writing onto a Binary File β Pickling!
To write a Python object into a binary file, use **`pickle.dump()`**. You can dump dictionaries, lists, tuples, strings, numbers β literally any Python object.
> [!RULE]
> **Syntax:**
> ```
> pickle.dump(object, file_object)
> ```
> - `object` β The Python object to serialize and store
> - `file_object` β File opened in `'wb'` (new) or `'ab'` (append) mode
```python
import pickle
# Writing a single dictionary record
f = open("students.dat", "wb")
student = {"name": "Aman Sharma", "roll": 1, "marks": 95}
pickle.dump(student, f)
f.close()
print("Record written! β ")
```
```python
import pickle
# Writing MULTIPLE records β one dump per record!
students = [
{"roll": 1, "name": "Aman Sharma", "marks": 95},
{"roll": 2, "name": "Priya Singh", "marks": 88},
{"roll": 3, "name": "Rohan Gupta", "marks": 76},
{"roll": 4, "name": "Sneha Joshi", "marks": 92},
]
f = open("students.dat", "wb")
for student in students:
pickle.dump(student, f) # Each dict dumped separately!
f.close()
print(f"{len(students)} records written! β ")
```
```python
import pickle
# Appending a NEW record without losing existing data
f = open("students.dat", "ab") # 'ab' = safe append
new_student = {"roll": 5, "name": "Kavya Reddy", "marks": 98}
pickle.dump(new_student, f)
f.close()
print("New student appended! β ")
```
> [!WARNING]
> **Common Mistake:** When writing multiple records with `pickle.dump()` in a loop, each call writes **one independent object**. Don't dump a single giant list if you plan to update individual records later β dump them **one by one** so you can read them back one by one with `pickle.load()`. π¨
> [!TIP]
> **Study Strategy:** Think of `pickle.dump()` like putting items into **individual sealed courier parcels** on a conveyor belt. Each object gets its own package. `pickle.load()` unpacks them one at a time from the other end! π¦
---
### π¦ 5.5.3 Reading from a Binary File β UnPickling!
To retrieve data from a binary file, use **`pickle.load()`**. Each call reads exactly **one object** that was previously dumped β in the same order they were written.
> [!RULE]
> **Syntax:**
> ```
> object = pickle.load(file_object)
> ```
> - `file_object` β File opened in `'rb'` mode
> - Returns the **next reconstructed Python object** from the file
> - Raises **`EOFError`** when no more objects remain (end of file)
```python
import pickle
# Reading a single record
f = open("students.dat", "rb")
student = pickle.load(f)
print(student)
# {'roll': 1, 'name': 'Aman Sharma', 'marks': 95}
f.close()
```
```python
import pickle
# π Reading ALL records β The Standard CBSE Pattern
f = open("students.dat", "rb")
print("All Student Records:")
print("-" * 40)
while True:
try:
student = pickle.load(f)
print(f"Roll: {student['roll']} | Name: {student['name']} | Marks: {student['marks']}")
except EOFError:
break # No more records β exit the loop cleanly
f.close()
```
> [!IMPORTANT]
> **Board Exam Hotspot:** The **`EOFError`** (End of File Error) is how Python signals that all objects have been read. The pattern below is **essential to memorize** β it appears in virtually every binary file reading/searching question:
> ```python
> while True:
> try:
> record = pickle.load(f)
> # process record
> except EOFError:
> break
> ```
> [!NOTE]
> **Memory Trick:**
> - `dump()` β **D**rop object **U**nder disk's **M**emory **P**rotection (write!)
> - `load()` β **L**ift **O**bject **A**nd **D**eliver back (read!)
>
> Together they're like a **courier service** β `dump()` ships the parcel, `load()` delivers it back to you! π
---
### π 5.5.4 Searching in a Binary File
Searching a binary file means reading records **one by one** and checking each against your criteria. There's no shortcut β you must **read β compare β act** for every record.
```mermaid
flowchart TD
A["Open file\nin 'rb' mode"]
B["pickle.load()\nRead one record"]
C{"Record matches\nthe search criteria?"}
D["β Found!\nDisplay / Process it"]
E{"EOFError\nraised?"}
F["β Record not found\nPrint message"]
G["Close file"]
A --> B
B --> C
C -->|"Yes β Match!"| D
C -->|"No β Keep looking"| E
D --> G
E -->|"No β more records"| B
E -->|"Yes β file ended"| F
F --> G
classDef normalNode fill:none;
classDef elseNode fill:none;
classDef exceptNode fill:none;
classDef tryNode fill:none;
class A normalNode;
class B tryNode;
class C normalNode;
class D elseNode;
class E normalNode;
class F exceptNode;
class G normalNode;
```
```python
import pickle
# Search for a student by roll number
def search_student(roll_no):
found = False
try:
f = open("students.dat", "rb")
while True:
try:
student = pickle.load(f)
if student["roll"] == roll_no:
print("\nβ Student Found!")
print(f" Name : {student['name']}")
print(f" Roll : {student['roll']}")
print(f" Marks : {student['marks']}")
found = True
break
except EOFError:
break
f.close()
except FileNotFoundError:
print("Error: File not found!")
if not found:
print(f"β No student with roll {roll_no} found.")
search_student(3)
```
```python
import pickle
# Search ALL students scoring above a cutoff (multiple matches)
def toppers(cutoff):
print(f"\nπ Students with marks > {cutoff}:")
print("-" * 35)
count = 0
f = open("students.dat", "rb")
while True:
try:
s = pickle.load(f)
if s["marks"] > cutoff:
print(f" {s['name']} β {s['marks']}")
count += 1
except EOFError:
break
f.close()
print(f"\nTotal: {count} student(s)")
toppers(85)
```
> [!TIP]
> **Study Strategy:** Every binary file search follows the **same 3-step skeleton** β master this and no question can beat you:
> 1. Open file in `'rb'` mode
> 2. `while True` β `try: pickle.load()` β check condition β `except EOFError: break`
> 3. Close file
>
> The only thing that changes between questions is the **condition inside step 2**! π―
---
### βοΈ 5.5.5 Updating in a Binary File
Unlike text files, **you cannot edit a single record inside a pickle binary file directly** β changing one record can shift byte positions and corrupt everything after it. The safe and standard CBSE approach is the **Read-All β Modify β Write-All-Back** pattern:
```mermaid
flowchart LR
A["Step 1οΈβ£\nOpen 'rb'\nRead ALL records into a list"]
B["Step 2οΈβ£\nFind & modify\nthe target record in the list"]
C["Step 3οΈβ£\nOpen 'wb'\nOverwrite file with modified list"]
D["β File Updated!"]
A --> B
B --> C
C --> D
classDef tryNode fill:none;
classDef elseNode fill:none;
classDef normalNode fill:none;
class A tryNode;
class B elseNode;
class C tryNode;
class D normalNode;
```
```python
import pickle
def update_marks(roll_no, new_marks):
# ββ Step 1: Read ALL records into a Python list ββ
records = []
try:
f = open("students.dat", "rb")
while True:
try:
records.append(pickle.load(f))
except EOFError:
break
f.close()
except FileNotFoundError:
print("Error: File not found!")
return
# ββ Step 2: Find and modify the target record ββββ
updated = False
for record in records:
if record["roll"] == roll_no:
print(f"Old marks for {record['name']}: {record['marks']}")
record["marks"] = new_marks
print(f"Updated marks: {record['marks']}")
updated = True
break
if not updated:
print(f"β Roll {roll_no} not found!")
return
# ββ Step 3: Write ALL records back to file βββββββ
f = open("students.dat", "wb") # 'wb' overwrites completely
for record in records:
pickle.dump(record, f)
f.close()
print("β Record updated successfully!")
update_marks(2, 95)
```
```python
import pickle
# Deleting a record β same Read-Modify-Write pattern!
def delete_student(roll_no):
records = []
f = open("students.dat", "rb")
while True:
try:
rec = pickle.load(f)
if rec["roll"] != roll_no: # Keep ALL except the target
records.append(rec)
except EOFError:
break
f.close()
f = open("students.dat", "wb") # Rewrite without deleted record
for rec in records:
pickle.dump(rec, f)
f.close()
print(f"β Student with roll {roll_no} deleted!")
delete_student(3)
```
> [!IMPORTANT]
> **Board Exam Hotspot:** The update/delete pattern is a **guaranteed exam question**. Always follow:
> 1. `'rb'` β Read all β store in list
> 2. Modify the list in memory
> 3. `'wb'` β Write entire modified list back
>
> Never attempt to overwrite a single record directly inside a pickle file! π¨
> [!WARNING]
> **Common Mistake:** Students try to use `'rb+'` mode thinking they can edit a specific record in place. **Don't!** Pickle records vary in size β overwriting one record shifts all byte positions and corrupts everything after it. Always use the read-all β modify β write-all approach! π₯
---
### π§© Complete Binary File Program β All Operations
```python
import pickle
FILENAME = "school.dat"
def create_records():
students = [
{"roll": 1, "name": "Aman Sharma", "marks": 95, "grade": "A+"},
{"roll": 2, "name": "Priya Singh", "marks": 88, "grade": "A"},
{"roll": 3, "name": "Rohan Gupta", "marks": 76, "grade": "B+"},
{"roll": 4, "name": "Sneha Joshi", "marks": 92, "grade": "A+"},
{"roll": 5, "name": "Kavya Reddy", "marks": 65, "grade": "B"},
]
f = open(FILENAME, "wb")
for s in students:
pickle.dump(s, f)
f.close()
print("β Records created!")
def display_all():
f = open(FILENAME, "rb")
print(f"\n{'Roll':<6} {'Name':<20} {'Marks':<8} {'Grade'}")
print("-" * 42)
while True:
try:
s = pickle.load(f)
print(f"{s['roll']:<6} {s['name']:<20} {s['marks']:<8} {s['grade']}")
except EOFError:
break
f.close()
def search_by_name(keyword):
f = open(FILENAME, "rb")
print(f"\nπ Searching for '{keyword}':")
found = False
while True:
try:
s = pickle.load(f)
if keyword.lower() in s["name"].lower():
print(f" β Roll {s['roll']}: {s['name']} | Marks: {s['marks']}")
found = True
except EOFError:
break
f.close()
if not found:
print(" No match found!")
def update_grade(roll, new_grade):
records = []
f = open(FILENAME, "rb")
while True:
try:
records.append(pickle.load(f))
except EOFError:
break
f.close()
for r in records:
if r["roll"] == roll:
r["grade"] = new_grade
f = open(FILENAME, "wb")
for r in records:
pickle.dump(r, f)
f.close()
print(f"β Grade updated for roll {roll}")
# Run all operations
create_records()
display_all()
search_by_name("Singh")
update_grade(3, "A")
display_all()
```
---
## π 5.6 Working with CSV Files
### What is a CSV File?
**CSV** stands for **Comma-Separated Values**. It's a universal, plain-text format for storing **structured tabular data** β just like an Excel spreadsheet, but in a simple `.csv` file.
Imagine this Excel table:
| Name | Roll | Marks | Grade |
|------|------|-------|-------|
| Aman | 1 | 95 | A+ |
| Priya | 2 | 88 | A |
In CSV format, that exact same data becomes:
```
Name,Roll,Marks,Grade
Aman,1,95,A+
Priya,2,88,A
```
That's it! Just values separated by commas, one row per line. Beautifully simple. π
> [!NOTE]
> **Memory Trick:** CSV = **C**ommas **S**eparating **V**alues! Every row is one line, every column is a comma-separated value. The simplest possible table format β yet accepted by Excel, Google Sheets, MySQL, PostgreSQL, and practically every tool on Earth! π
- **Why CSV over plain text files? π**
* **Structure** β Data organized in rows and columns β not just random lines
* **Universal** β Every tool understands CSV: Excel, databases, Python, R, Java...
* **Human-readable** β Unlike binary, you can open and read it in Notepad
* **Lightweight** β No database software needed β just a `.csv` file!
* **Perfect for** β Student records, marks sheets, sales data, inventory, logs
- **Python's `csv` Module π**
* Built-in module β no installation needed (`import csv`)
* Handles commas **inside values** automatically using quoting
* `csv.writer` / `csv.reader` β list-based read/write
* `csv.DictWriter` / `csv.DictReader` β dictionary-based read/write (cleaner!)
> [!RULE]
> **Always import csv at the top:**
> ```python
> import csv
> ```
---
### π 5.6.1 Opening / Closing CSV Files
CSV files are opened with the same `open()` function β with one critical extra argument:
> [!RULE]
> **The Standard CSV Opening Pattern:**
> ```python
> f = open("data.csv", "w", newline='')
> ```
> The **`newline=''`** parameter is essential β it prevents Python from adding extra blank lines between rows (especially on Windows)!
- **Modes for CSV files π**
* `'w'` β Write (creates new / overwrites existing)
* `'r'` β Read (file must exist)
* `'a'` β Append (adds to end, preserves existing data)
```python
import csv
# Writing β always newline=''
f = open("marks.csv", "w", newline='')
# ... write operations ...
f.close()
# Reading β good practice to include newline='' here too
f = open("marks.csv", "r", newline='')
# ... read operations ...
f.close()
# Best practice β with statement (auto-closes, cleaner!)
with open("marks.csv", "w", newline='') as f:
pass # operations here
```
> [!WARNING]
> **Common Mistake:** Forgetting `newline=''` when **writing** CSV on Windows inserts a **blank line between every data row**! This breaks the file structure and trips up every tool trying to read it. Always include `newline=''` β no exceptions! π¨
---
### βοΈ 5.6.2 Writing in CSV Files
Two approaches β choose based on whether you're working with lists or dictionaries:
- **βοΈ `csv.writer` + `writerow()` / `writerows()`**
* Takes **lists or tuples** β each list is one row
* `writerow(list)` β Write a **single row**
* `writerows(list_of_lists)` β Write **all rows** at once β efficient!
- **π `csv.DictWriter` + `writeheader()` + `writerow()`**
* Takes **dictionaries** β keys become column headers automatically
* `writeheader()` β Write the first row as column names
* `writerow(dict)` β Write one dict as one data row
#### Method 1 β `csv.writer` (List-based)
```python
import csv
# writerow() β one row at a time
with open("students.csv", "w", newline='') as f:
writer = csv.writer(f)
writer.writerow(["Roll", "Name", "Marks", "Grade"]) # Header
writer.writerow([1, "Aman Sharma", 95, "A+"])
writer.writerow([2, "Priya Singh", 88, "A"])
writer.writerow([3, "Rohan Gupta", 76, "B+"])
print("CSV written! β ")
# File (students.csv):
# Roll,Name,Marks,Grade
# 1,Aman Sharma,95,A+
# 2,Priya Singh,88,A
# 3,Rohan Gupta,76,B+
```
```python
import csv
# writerows() β write ALL rows in one efficient call!
with open("students.csv", "w", newline='') as f:
writer = csv.writer(f)
header = ["Roll", "Name", "Marks", "Grade"]
data = [
[1, "Aman Sharma", 95, "A+"],
[2, "Priya Singh", 88, "A"],
[3, "Rohan Gupta", 76, "B+"],
[4, "Sneha Joshi", 92, "A+"],
[5, "Kavya Reddy", 65, "B"],
]
writer.writerow(header) # Header first
writer.writerows(data) # All data in one go!
print("All records written with writerows()! β ")
```
#### Method 2 β `csv.DictWriter` (Dictionary-based)
```python
import csv
# DictWriter β cleaner, safer for complex data
with open("students.csv", "w", newline='') as f:
fields = ["Roll", "Name", "Marks", "Grade"]
writer = csv.DictWriter(f, fieldnames=fields)
writer.writeheader() # Writes: Roll,Name,Marks,Grade
students = [
{"Roll": 1, "Name": "Aman Sharma", "Marks": 95, "Grade": "A+"},
{"Roll": 2, "Name": "Priya Singh", "Marks": 88, "Grade": "A"},
{"Roll": 3, "Name": "Rohan Gupta", "Marks": 76, "Grade": "B+"},
]
for s in students:
writer.writerow(s)
print("DictWriter done! β ")
```
> [!IMPORTANT]
> **Board Exam Hotspot β `write` methods summary:**
> | Method | Used With | Writes |
> |--------|-----------|--------|
> | `writerow(list)` | `csv.writer` | One row as a list |
> | `writerows(list_of_lists)` | `csv.writer` | Multiple rows at once |
> | `writeheader()` | `csv.DictWriter` only | Column names as first row |
> | `writerow(dict)` | `csv.DictWriter` | One row as a dictionary |
---
### π 5.6.3 Reading in CSV Files
Reading mirrors writing β two clean approaches:
- π **`csv.reader`** β Reads each row as a **list of strings**
- π **`csv.DictReader`** β Reads each row as a **dictionary** (first row = keys)
#### Method 1 β `csv.reader` (List-based)
```python
import csv
# Basic reading β each row is a list
with open("students.csv", "r", newline='') as f:
reader = csv.reader(f)
for row in reader:
print(row)
# Output:
# ['Roll', 'Name', 'Marks', 'Grade'] β header row (a list!)
# ['1', 'Aman Sharma', '95', 'A+'] β all values are STRINGS!
# ['2', 'Priya Singh', '88', 'A']
```
```python
import csv
# Skip header, then process data rows
with open("students.csv", "r", newline='') as f:
reader = csv.reader(f)
header = next(reader) # Read and skip the header row
print(f"Columns: {header}")
for row in reader:
roll = int(row[0]) # β Convert string to int!
name = row[1]
marks = int(row[2]) # β CSV gives strings β always convert!
grade = row[3]
print(f"Roll {roll}: {name} scored {marks} marks ({grade})")
```
#### Method 2 β `csv.DictReader` (Dictionary-based β Recommended!)
```python
import csv
# DictReader β access columns by name, not index
with open("students.csv", "r", newline='') as f:
reader = csv.DictReader(f) # First row auto-becomes dict keys!
for row in reader:
# Access by column name β much cleaner than row[0], row[2]!
print(f"{row['Name']} β Marks: {row['Marks']}, Grade: {row['Grade']}")
```
> [!WARNING]
> **Common Mistake:** `csv.reader` reads **everything as strings** β even numbers! `95` becomes `'95'`. Always convert:
> ```python
> marks = int(row[2]) # β '95' β 95
> price = float(row[3]) # β '45.5' β 45.5
> ```
> Forgetting this breaks any arithmetic and causes `TypeError`! π¨
> [!NOTE]
> **Memory Trick:**
> - `csv.writer` / `csv.reader` β Work with **lists** (access by position: `row[0]`, `row[1]`)
> - `csv.DictWriter` / `csv.DictReader` β Work with **dicts** (access by name: `row['Name']`, `row['Marks']`)
>
> The **Dict** variants are cleaner, safer, and far more readable. Prefer them whenever possible! π
---
### βοΈ CSV Write Operation β Visual Guide
```mermaid
flowchart LR
A["π Python Data\nLists / Dicts"]
B["open() with\nnewline=''"]
C["csv.writer\nor DictWriter"]
D["writerow()\nwriterows()\nwriteheader()"]
E["πΎ CSV File\nstudents.csv"]
A --> B --> C --> D --> E
classDef dataNode fill:none;
classDef fileNode fill:none;
class A,E dataNode;
class B,C,D fileNode;
```
### π CSV Read Operation β Visual Guide
```mermaid
flowchart LR
E["πΎ CSV File\nstudents.csv"]
F["open() with\nnewline=''"]
G["csv.reader\nor DictReader"]
H["for row in reader\nProcess each row"]
I["β Restored Data\n(convert types!)"]
E --> F --> G --> H --> I
classDef dataNode fill:none;
classDef fileNode fill:none;
class E,I dataNode;
class F,G,H fileNode;
```
---
### π§© Complete CSV Program β All Operations
```python
import csv
FILENAME = "library.csv"
FIELDS = ["BookID", "Title", "Author", "Price", "Qty"]
def create_library():
"""Write initial records"""
books = [
[101, "Python Basics", "Guido Van", 450, 15],
[102, "Data Structures", "Mark Allen", 620, 8],
[103, "Computer Networks", "Forouzan", 780, 12],
[104, "DBMS Concepts", "Korth", 590, 5],
[105, "Artificial Intelligence","Russell", 850, 10],
]
with open(FILENAME, "w", newline='') as f:
writer = csv.writer(f)
writer.writerow(FIELDS) # Header
writer.writerows(books) # All data
print("β Library CSV created!")
def display_all():
"""Read and display all books"""
with open(FILENAME, "r", newline='') as f:
reader = csv.DictReader(f)
print(f"\n{'ID':<6} {'Title':<28} {'Author':<16} {'Price':>7} {'Qty':>5}")
print("-" * 65)
for row in reader:
print(f"{row['BookID']:<6} {row['Title']:<28} {row['Author']:<16} βΉ{row['Price']:>5} {row['Qty']:>5}")
def search_by_author(name):
"""Search books by author name"""
print(f"\nπ Books by '{name}':")
found = False
with open(FILENAME, "r", newline='') as f:
reader = csv.DictReader(f)
for row in reader:
if name.lower() in row["Author"].lower():
print(f" β {row['Title']} (βΉ{row['Price']})")
found = True
if not found:
print(" No books found!")
def add_book(bid, title, author, price, qty):
"""Append a new book"""
with open(FILENAME, "a", newline='') as f:
writer = csv.writer(f)
writer.writerow([bid, title, author, price, qty])
print(f"β '{title}' added to library!")
# Run all operations
create_library()
display_all()
search_by_author("Korth")
add_book(106, "Operating Systems", "Silberschatz", 710, 7)
display_all()
```
---
## π Binary File vs CSV File β Side-by-Side
> [!IMPORTANT]
> **Board Exam Hotspot β The Must-Know Comparison Table:**
>
> | Feature | Binary File (`pickle`) | CSV File (`csv`) |
> |---------|----------------------|-----------------|
> | Module | `pickle` | `csv` |
> | Human-readable? | β No | β Yes |
> | Stores Python objects? | β Any object | β οΈ Strings only |
> | Opens in Excel? | β No | β Yes |
> | File mode | `'rb'`, `'wb'`, `'ab'` | `'r'`, `'w'`, `'a'` |
> | Write function | `pickle.dump()` | `writerow()` / `writerows()` |
> | Read function | `pickle.load()` | `for row in reader:` |
> | End of file detection | `EOFError` exception | Loop ends naturally |
> | Type preservation | β int, list, dict intact | β Everything becomes string |
> | Special parameter | None | `newline=''` in `open()` |
---
## π Rapid Revision β Board Exam Ready!
> [!TIP]
> **Study Strategy β 6 Must-Practise CBSE Questions:**
> 1. ποΈ "What is pickling? Write a program to write and read a dictionary using pickle"
> 2. ποΈ "Write a program to search a record in a binary file by a given field"
> 3. ποΈ "Write a program to update/modify a record in a binary file"
> 4. ποΈ "Difference between text file, binary file, and CSV file"
> 5. ποΈ "Write a program to write data into a CSV file using csv.writer"
> 6. ποΈ "Why is `newline=''` used when opening a CSV file in Python?"
> [!WARNING]
> **Common Mistakes β Final Exam Checklist:**
> 1. β Forgetting `import pickle` or `import csv`
> 2. β Opening binary files in text mode (`'w'` instead of `'wb'`)
> 3. β Not handling `EOFError` in binary file reading loops
> 4. β Forgetting `newline=''` in CSV `open()` β causes blank rows
> 5. β Not converting CSV string values to `int`/`float` before arithmetic
> 6. β Trying to update a binary file record in-place (using `'rb+'`) instead of the read-all β modify β write-all pattern
```python
# βοΈ Bonus Program: Export Binary File β CSV File
# The ultimate combo question β often seen in CBSE practicals!
import pickle, csv
# Step 1: Read all records from binary file
records = []
with open("students.dat", "rb") as bf:
while True:
try:
records.append(pickle.load(bf))
except EOFError:
break
# Step 2: Write all records to CSV
with open("students.csv", "w", newline='') as cf:
if records:
writer = csv.DictWriter(cf, fieldnames=records[0].keys())
writer.writeheader()
writer.writerows(records)
print(f"β Exported {len(records)} records from binary (.dat) to CSV!")
```