python
exercises
exercises.py🐍python
"""
07 - File Handling: Exercises
Complete these exercises to practice file operations!
"""
import os
import json
import csv
import tempfile
from pathlib import Path
from typing import Optional
# =============================================================================
# EXERCISE 1: Basic File Reading
# =============================================================================
def read_entire_file(filepath: str) -> str:
"""
Read and return the entire contents of a file.
Args:
filepath: Path to the file to read
Returns:
The complete file contents as a string
Raises:
FileNotFoundError: If file doesn't exist
"""
# YOUR CODE HERE
with open(filepath, 'r') as f:
return f.read()
def read_lines_as_list(filepath: str) -> list[str]:
"""
Read a file and return lines as a list (without newline characters).
Args:
filepath: Path to the file to read
Returns:
List of lines without trailing newlines
"""
# YOUR CODE HERE
with open(filepath, 'r') as f:
return f.read().splitlines()
def count_lines(filepath: str) -> int:
"""
Count the total number of lines in a file.
Args:
filepath: Path to the file
Returns:
Number of lines
"""
# YOUR CODE HERE
with open(filepath, 'r') as f:
return sum(1 for _ in f)
# =============================================================================
# EXERCISE 2: Basic File Writing
# =============================================================================
def write_lines(filepath: str, lines: list[str]) -> None:
"""
Write a list of strings to a file, one per line.
Args:
filepath: Path to write to
lines: List of strings to write
"""
# YOUR CODE HERE
with open(filepath, 'w') as f:
for line in lines:
f.write(line + '\n')
def append_to_file(filepath: str, text: str) -> None:
"""
Append text to the end of a file.
Args:
filepath: Path to the file
text: Text to append (add a newline at the end)
"""
# YOUR CODE HERE
with open(filepath, 'a') as f:
f.write(text + '\n')
# =============================================================================
# EXERCISE 3: Find and Replace
# =============================================================================
def find_and_replace(filepath: str, old_text: str, new_text: str) -> int:
"""
Replace all occurrences of old_text with new_text in a file.
Args:
filepath: Path to the file
old_text: Text to find
new_text: Text to replace with
Returns:
Number of replacements made
"""
# YOUR CODE HERE
with open(filepath, 'r') as f:
content = f.read()
count = content.count(old_text)
new_content = content.replace(old_text, new_text)
with open(filepath, 'w') as f:
f.write(new_content)
return count
# =============================================================================
# EXERCISE 4: Word Counter
# =============================================================================
def count_words(filepath: str) -> dict[str, int]:
"""
Count occurrences of each word in a file.
Words should be lowercase and stripped of punctuation.
Args:
filepath: Path to the file
Returns:
Dictionary mapping words to counts
"""
# YOUR CODE HERE
import string
with open(filepath, 'r') as f:
content = f.read().lower()
# Remove punctuation
translator = str.maketrans('', '', string.punctuation)
content = content.translate(translator)
words = content.split()
word_counts: dict[str, int] = {}
for word in words:
word_counts[word] = word_counts.get(word, 0) + 1
return word_counts
def most_common_words(filepath: str, n: int = 5) -> list[tuple[str, int]]:
"""
Find the n most common words in a file.
Args:
filepath: Path to the file
n: Number of top words to return
Returns:
List of (word, count) tuples sorted by count descending
"""
# YOUR CODE HERE
counts = count_words(filepath)
sorted_words = sorted(counts.items(), key=lambda x: x[1], reverse=True)
return sorted_words[:n]
# =============================================================================
# EXERCISE 5: JSON Operations
# =============================================================================
def read_json_file(filepath: str) -> dict:
"""
Read and parse a JSON file.
Args:
filepath: Path to the JSON file
Returns:
Parsed JSON data as dictionary
"""
# YOUR CODE HERE
with open(filepath, 'r') as f:
return json.load(f)
def write_json_file(filepath: str, data: dict, pretty: bool = True) -> None:
"""
Write data to a JSON file.
Args:
filepath: Path to write to
data: Data to write
pretty: If True, format with indentation
"""
# YOUR CODE HERE
with open(filepath, 'w') as f:
if pretty:
json.dump(data, f, indent=2)
else:
json.dump(data, f)
def update_json_value(filepath: str, key: str, value) -> None:
"""
Update a specific key in a JSON file.
Args:
filepath: Path to the JSON file
key: Key to update
value: New value
"""
# YOUR CODE HERE
data = read_json_file(filepath)
data[key] = value
write_json_file(filepath, data)
# =============================================================================
# EXERCISE 6: CSV Operations
# =============================================================================
def read_csv_as_dicts(filepath: str) -> list[dict]:
"""
Read a CSV file and return as list of dictionaries.
Args:
filepath: Path to CSV file
Returns:
List of dictionaries (one per row)
"""
# YOUR CODE HERE
with open(filepath, 'r') as f:
reader = csv.DictReader(f)
return list(reader)
def write_dicts_to_csv(filepath: str, data: list[dict]) -> None:
"""
Write a list of dictionaries to a CSV file.
Args:
filepath: Path to write to
data: List of dictionaries with same keys
"""
# YOUR CODE HERE
if not data:
return
fieldnames = list(data[0].keys())
with open(filepath, 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(data)
def filter_csv_rows(filepath: str, column: str, value: str) -> list[dict]:
"""
Filter CSV rows where column equals value.
Args:
filepath: Path to CSV file
column: Column name to filter on
value: Value to match
Returns:
List of matching rows as dictionaries
"""
# YOUR CODE HERE
data = read_csv_as_dicts(filepath)
return [row for row in data if row.get(column) == value]
# =============================================================================
# EXERCISE 7: Pathlib Operations
# =============================================================================
def list_files_by_extension(directory: str, extension: str) -> list[Path]:
"""
List all files in directory with given extension.
Args:
directory: Directory to search
extension: File extension (e.g., '.txt')
Returns:
List of Path objects for matching files
"""
# YOUR CODE HERE
if not extension.startswith('.'):
extension = '.' + extension
dir_path = Path(directory)
return list(dir_path.glob(f'*{extension}'))
def find_files_recursive(directory: str, pattern: str) -> list[Path]:
"""
Recursively find all files matching a glob pattern.
Args:
directory: Root directory to search
pattern: Glob pattern (e.g., '*.py')
Returns:
List of matching Path objects
"""
# YOUR CODE HERE
dir_path = Path(directory)
return list(dir_path.rglob(pattern))
def get_file_info(filepath: str) -> dict:
"""
Get information about a file.
Args:
filepath: Path to the file
Returns:
Dictionary with keys: 'name', 'size', 'extension', 'exists'
"""
# YOUR CODE HERE
p = Path(filepath)
return {
'name': p.name,
'size': p.stat().st_size if p.exists() else 0,
'extension': p.suffix,
'exists': p.exists()
}
# =============================================================================
# EXERCISE 8: Log File Parser
# =============================================================================
def parse_log_line(line: str) -> Optional[dict]:
"""
Parse a log line in format: [YYYY-MM-DD HH:MM:SS] LEVEL: message
Args:
line: Log line string
Returns:
Dictionary with 'timestamp', 'level', 'message' or None if invalid
"""
# YOUR CODE HERE
import re
pattern = r'\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+): (.+)'
match = re.match(pattern, line.strip())
if match:
return {
'timestamp': match.group(1),
'level': match.group(2),
'message': match.group(3)
}
return None
def filter_log_by_level(filepath: str, level: str) -> list[dict]:
"""
Filter log entries by level (e.g., 'ERROR', 'WARNING').
Args:
filepath: Path to log file
level: Log level to filter
Returns:
List of matching log entries as dictionaries
"""
# YOUR CODE HERE
results = []
with open(filepath, 'r') as f:
for line in f:
parsed = parse_log_line(line)
if parsed and parsed['level'] == level:
results.append(parsed)
return results
# =============================================================================
# EXERCISE 9: File Backup
# =============================================================================
def create_backup(filepath: str) -> str:
"""
Create a backup of a file by copying to filename.bak
Args:
filepath: Path to the file to backup
Returns:
Path to the backup file
"""
# YOUR CODE HERE
import shutil
backup_path = filepath + '.bak'
shutil.copy2(filepath, backup_path)
return backup_path
def create_numbered_backup(filepath: str) -> str:
"""
Create numbered backups (file.bak.1, file.bak.2, etc.)
Find the next available number.
Args:
filepath: Path to the file to backup
Returns:
Path to the backup file
"""
# YOUR CODE HERE
import shutil
n = 1
while True:
backup_path = f"{filepath}.bak.{n}"
if not os.path.exists(backup_path):
break
n += 1
shutil.copy2(filepath, backup_path)
return backup_path
# =============================================================================
# EXERCISE 10: Configuration Manager
# =============================================================================
class ConfigManager:
"""Manage application configuration stored in JSON file."""
def __init__(self, config_path: str):
"""
Initialize with path to config file.
Create with empty dict if doesn't exist.
"""
# YOUR CODE HERE
self.config_path = config_path
if os.path.exists(config_path):
with open(config_path, 'r') as f:
self.config = json.load(f)
else:
self.config = {}
self._save()
def _save(self) -> None:
"""Save config to file."""
with open(self.config_path, 'w') as f:
json.dump(self.config, f, indent=2)
def get(self, key: str, default=None):
"""
Get a configuration value.
Args:
key: Configuration key
default: Default value if key not found
Returns:
Configuration value or default
"""
# YOUR CODE HERE
return self.config.get(key, default)
def set(self, key: str, value) -> None:
"""
Set a configuration value and save.
Args:
key: Configuration key
value: Value to set
"""
# YOUR CODE HERE
self.config[key] = value
self._save()
def delete(self, key: str) -> bool:
"""
Delete a configuration key.
Args:
key: Key to delete
Returns:
True if key existed, False otherwise
"""
# YOUR CODE HERE
if key in self.config:
del self.config[key]
self._save()
return True
return False
# =============================================================================
# TEST YOUR SOLUTIONS
# =============================================================================
if __name__ == "__main__":
import tempfile
import shutil
# Create temporary directory for tests
temp_dir = tempfile.mkdtemp()
try:
print("Testing file handling exercises...")
print("=" * 60)
# Test 1: Basic reading
test_file = os.path.join(temp_dir, "test.txt")
with open(test_file, 'w') as f:
f.write("Line 1\nLine 2\nLine 3\n")
content = read_entire_file(test_file)
assert "Line 1" in content, "read_entire_file failed"
print("✓ read_entire_file")
lines = read_lines_as_list(test_file)
assert lines == ["Line 1", "Line 2", "Line 3"], "read_lines_as_list failed"
print("✓ read_lines_as_list")
assert count_lines(test_file) == 3, "count_lines failed"
print("✓ count_lines")
# Test 2: Writing
write_file = os.path.join(temp_dir, "write_test.txt")
write_lines(write_file, ["Hello", "World"])
assert os.path.exists(write_file), "write_lines failed"
print("✓ write_lines")
append_to_file(write_file, "Appended")
with open(write_file, 'r') as f:
assert "Appended" in f.read(), "append_to_file failed"
print("✓ append_to_file")
# Test 3: Find and replace
replace_file = os.path.join(temp_dir, "replace.txt")
with open(replace_file, 'w') as f:
f.write("hello world, hello python")
count = find_and_replace(replace_file, "hello", "hi")
assert count == 2, "find_and_replace count failed"
print("✓ find_and_replace")
# Test 4: Word counter
word_file = os.path.join(temp_dir, "words.txt")
with open(word_file, 'w') as f:
f.write("the quick brown fox the quick dog")
counts = count_words(word_file)
assert counts.get('the') == 2, "count_words failed"
print("✓ count_words")
top = most_common_words(word_file, 2)
assert len(top) == 2, "most_common_words failed"
print("✓ most_common_words")
# Test 5: JSON
json_file = os.path.join(temp_dir, "test.json")
write_json_file(json_file, {"name": "test", "value": 42})
data = read_json_file(json_file)
assert data["name"] == "test", "JSON read/write failed"
print("✓ JSON operations")
update_json_value(json_file, "value", 100)
data = read_json_file(json_file)
assert data["value"] == 100, "update_json_value failed"
print("✓ update_json_value")
# Test 6: CSV
csv_file = os.path.join(temp_dir, "test.csv")
csv_data = [
{"name": "Alice", "age": "30"},
{"name": "Bob", "age": "25"}
]
write_dicts_to_csv(csv_file, csv_data)
read_data = read_csv_as_dicts(csv_file)
assert len(read_data) == 2, "CSV operations failed"
print("✓ CSV operations")
filtered = filter_csv_rows(csv_file, "name", "Alice")
assert len(filtered) == 1, "filter_csv_rows failed"
print("✓ filter_csv_rows")
# Test 7: Pathlib
txt_files = list_files_by_extension(temp_dir, '.txt')
assert len(txt_files) >= 1, "list_files_by_extension failed"
print("✓ list_files_by_extension")
info = get_file_info(test_file)
assert info['exists'] is True, "get_file_info failed"
print("✓ get_file_info")
# Test 8: Log parser
log_file = os.path.join(temp_dir, "app.log")
with open(log_file, 'w') as f:
f.write("[2024-01-15 10:30:00] INFO: Started\n")
f.write("[2024-01-15 10:30:01] ERROR: Something failed\n")
parsed = parse_log_line("[2024-01-15 10:30:00] INFO: Test message")
assert parsed['level'] == 'INFO', "parse_log_line failed"
print("✓ parse_log_line")
errors = filter_log_by_level(log_file, 'ERROR')
assert len(errors) == 1, "filter_log_by_level failed"
print("✓ filter_log_by_level")
# Test 9: Backup
backup_path = create_backup(test_file)
assert os.path.exists(backup_path), "create_backup failed"
print("✓ create_backup")
numbered = create_numbered_backup(test_file)
assert '.bak.' in numbered, "create_numbered_backup failed"
print("✓ create_numbered_backup")
# Test 10: Config manager
config_file = os.path.join(temp_dir, "config.json")
config = ConfigManager(config_file)
config.set("theme", "dark")
assert config.get("theme") == "dark", "ConfigManager set/get failed"
print("✓ ConfigManager")
print("\n" + "=" * 60)
print("All tests passed! ✓")
finally:
# Cleanup
shutil.rmtree(temp_dir)