Day 8 – String Manipulation and Formatting


 ๐Ÿงต Day 8 – String Manipulation and Formatting

1. Learning Objectives

By the end of Day 8, you will be able to:

  • Use common string methods (.upper(), .lower(), .strip(), .split(), .join(), .replace(), .find(), .startswith()).
  • Format strings using f‑strings, .format(), and %‑formatting.
  • Generate formatted reports (e.g., material take‑offs, beam schedules).
  • Work with part marks, drawing numbers, and naming conventions.
  • Clean and standardise user‑input strings for robust AEC scripts.

2. Concept Explanation

2.1 Why Strings Matter in AEC

Strings are everywhere in AEC practice:

  • Drawing numbers: "A-101", "S-202-RevB"
  • Material names: "Concrete C30", "Steel Grade 355"
  • Part marks: "UB 406×178×60", "B1", "COL-12"
  • Project codes: "PROJ-2026-045"
  • User input that needs cleaning (e.g., " Concrete " → "Concrete")

Being able to manipulate and format strings is essential for generating professional output and parsing data from external sources.

2.2 Common String Methods

MethodDescriptionAEC Example
.upper()Convert to uppercase"beam".upper() → "BEAM"
.lower()Convert to lowercase"STEEL".lower() → "steel"
.strip()Remove leading/trailing whitespace" Concrete ".strip() → "Concrete"
.split(sep)Split into list by separator"UB 406×178×60".split("×") → ['UB 406', '178', '60']
.join(iterable)Join list elements into string"-".join(["A","101"]) → "A-101"
.replace(old,new)Replace all occurrences"S-201".replace("-","/") → "S/201"
.find(sub)Return index of first occurrence (-1 if not found)"Concrete C30".find("C30") → 9
.startswith(pre)Check if string starts with prefix"S-202".startswith("S") → True
.endswith(suf)Check if string ends with suffix"drawing.pdf".endswith(".pdf") → True

2.3 String Formatting Options

f‑strings (Python 3.6+, recommended)

beam_mark = "B1"
span = 6.0
load = 25.0
print(f"Beam {beam_mark}: span = {span:.1f} m, load = {load:.1f} kN/m")

.format() method

print("Beam {}: span = {:.1f} m, load = {:.1f} kN/m".format(beam_mark, span, load))

%‑formatting (older style)

print("Beam %s: span = %.1f m, load = %.1f kN/m" % (beam_mark, span, load))

Alignment and width specifiers (f‑string):

# Left (<), Right (>), Center (^)
print(f"{'Mark':<10} {'Span':>8} {'Load':>8}")
print(f"{'B1':<10} {6.0:>8.1f} {25.0:>8.1f}")

3. Code Examples

Example 1: Standardising material names from user input

# User might type "concrete", "Concrete", "  CONCRETE  " etc.
raw_material = input("Enter material type: ")
clean_material = raw_material.strip().lower()
print(f"Standardised: {clean_material}")

# Check against known materials
known_materials = ["concrete", "steel", "timber", "masonry"]
if clean_material in known_materials:
    print("Valid material.")
else:
    print("Unknown material – please check specification.")

Example 2: Parsing a beam mark into components

# Typical beam mark format: "B-01", "B-12", "B1" – but can vary
beam_mark = "B1-12"

# Split by '-' or use indexing
parts = beam_mark.split("-")
print(parts)   # ['B1', '12'] if hyphen present

# More robust parsing
if "-" in beam_mark:
    prefix, number = beam_mark.split("-")
else:
    prefix = beam_mark[0]
    number = beam_mark[1:]

print(f"Prefix: {prefix}, Number: {number}")

Example 3: Generating a formatted beam schedule

beams = [
    {"mark": "B1", "span": 6.0, "load": 25, "depth": 400},
    {"mark": "B2", "span": 4.5, "load": 18, "depth": 350},
    {"mark": "B3", "span": 7.2, "load": 30, "depth": 500},
]

# Column headers with alignment
print(f"{'Beam':<8} {'Span (m)':<10} {'Load (kN/m)':<12} {'Depth (mm)':<10}")
print("-" * 40)

for b in beams:
    print(f"{b['mark']:<8} {b['span']:<10.1f} {b['load']:<12.1f} {b['depth']:<10}")

Output:

Beam     Span (m)   Load (kN/m)  Depth (mm)
----------------------------------------
B1       6.0        25.0         400
B2       4.5        18.0         350
B3       7.2        30.0         500

Example 4: Working with drawing number revisions

# Drawing register management
drawing = "S-202-RevB"

# Check discipline
if drawing.startswith("S-"):
    discipline = "Structural"
elif drawing.startswith("A-"):
    discipline = "Architectural"
elif drawing.startswith("M-"):
    discipline = "Mechanical"
elif drawing.startswith("E-"):
    discipline = "Electrical"
else:
    discipline = "Unknown"

print(f"Drawing {drawing} → Discipline: {discipline}")

# Extract revision
if "Rev" in drawing:
    parts = drawing.split("Rev")
    rev_letter = parts[-1]
    print(f"Revision: {rev_letter}")
else:
    print("No revision found")

Example 5: Building a part mark from components

# Generate a standard steel member tag
member_type = "UB"
depth = 406
weight = 60
length = 8.5

# Format: "UB-406x178x60-L=8.5m"
part_mark = f"{member_type}-{depth}x178x{weight}-L={length}m"
print(part_mark)   # "UB-406x178x60-L=8.5m"

# Or with padding for a table
print(f"{member_type:<4} {depth:>4} x 178 x {weight:<4}  L = {length:.1f} m")

4. Hands‑on Exercises (3–5 Problems)

Problem 1 – Name cleaning
Ask the user to enter a material name (e.g., " ConCrete ").
Use .strip().lower() to clean it, then check if it's in a list of acceptable materials. Print appropriate messages.

Problem 2 – Drawing number parser
Given a drawing number like "STR-101-RevC", write code that:

  • Extracts the discipline prefix ("STR")
  • Extracts the number ("101")
  • Extracts the revision ("C")
  • Prints each part on a separate line.

Hint: use .split("-").

Problem 3 – Formatted quantity take‑off
You have a list of dictionaries representing material quantities:

materials = [
    {"name": "Concrete C30", "volume": 45.2, "unit": "m³"},
    {"name": "Rebar 20mm", "mass": 12.5, "unit": "tonnes"},
    {"name": "Steel UB 406", "mass": 8.3, "unit": "tonnes"},
]

Print a nicely formatted table with aligned columns:

Material           Quantity   Unit
----------------------------------------
Concrete C30         45.20   m³
Rebar 20mm           12.50   tonnes
Steel UB 406          8.30   tonnes

Problem 4 – Joining floor names
You have a list of floor names: ["Ground", "Level 2", "Level 3", "Roof"].
Use .join() to create a single string: "Ground | Level 2 | Level 3 | Roof".
Then replace "Level " with "L" using .replace() to get: "Ground | L2 | L3 | Roof".

Solutions (attempt first):

# P1
acceptable = ["concrete", "steel", "timber", "masonry"]
raw = input("Enter material: ")
clean = raw.strip().lower()
if clean in acceptable:
    print(f"{clean} is accepted.")
else:
    print(f"{clean} is not in the approved list.")

# P2
drawing = "STR-101-RevC"
parts = drawing.split("-")
discipline = parts[0]
number = parts[1]
revision = parts[2].replace("Rev", "")
print(f"Discipline: {discipline}")
print(f"Number: {number}")
print(f"Revision: {revision}")

# P3
materials = [
    {"name": "Concrete C30", "volume": 45.2, "unit": "m³"},
    {"name": "Rebar 20mm", "mass": 12.5, "unit": "tonnes"},
    {"name": "Steel UB 406", "mass": 8.3, "unit": "tonnes"},
]
print(f"{'Material':<20} {'Quantity':<10} {'Unit':<10}")
print("-" * 40)
for m in materials:
    qty = m.get("volume") or m.get("mass")
    print(f"{m['name']:<20} {qty:<10.2f} {m['unit']:<10}")

# P4
floors = ["Ground", "Level 2", "Level 3", "Roof"]
combined = " | ".join(floors)
print(combined)
short = combined.replace("Level ", "L")
print(short)

5. Applied Challenge Task

Task: Drawing Register & Report Generator

You are given a raw list of drawing information as strings:

raw_drawings = [
    "A-101-Ground Floor Plan",
    "S-201-Foundation Plan-RevB",
    "M-301-HVAC Layout",
    "E-401-Lighting Plan",
    "S-202-First Floor Framing-RevA",
    "A-102-First Floor Plan",
]

Write a script that:

  1. Parses each string into components:

    • Discipline (first character or prefix)
    • Drawing number
    • Title
    • Revision (if present, e.g., "RevB", "RevA")
  2. Stores the data in a list of dictionaries with keys: discipline, number, title, revision.

  3. Prints a formatted register table with columns: Discipline, Number, Title, Revision.

  4. Counts how many drawings belong to each discipline (using a dictionary).

  5. Lists all unique revision letters found.

Bonus:
Allow the user to filter by discipline: e.g., input "S" and show only structural drawings.

Why this matters:
This task mimics real‑world document management in AEC projects. You'll use string splitting, joining, formatting, and dictionary accumulation – all essential for handling project data.


6. Brief Review Summary

  • String methods: .strip(), .lower(), .upper(), .split(), .join(), .replace(), .startswith(), .find().
  • f‑strings: f"{value:width.precision}" for aligned, formatted output.
  • .format() and % formatting are alternatives.
  • Clean user input before processing (strip + lower).
  • Parsing structured strings (e.g., drawing numbers) is a common AEC task.
  • Formatted tables improve readability of reports.

Key takeaway:
String manipulation turns messy, human‑generated data into clean, structured information you can process and present professionally – an essential skill for any AEC automation workflow.


7. Preview of Next Topic (Day 9)

Tomorrow we’ll cover File Handling – Reading/Writing CSV, TXT, and Excel files (pandas introduction).
You’ll learn:

  • Opening and reading text files (.txt, .csv).
  • Writing reports to files.
  • Introduction to pandas for reading/writing Excel spreadsheets.
  • Practical example: reading a table of structural members from a CSV and performing calculations.

File I/O is what connects your Python scripts to real project data – you'll be able to import existing schedules and export results.

Comments

EARN AT THE COMFORT OF YOUR HOME

Sponsored content

Popular posts from this blog

Day 5 – Functions, Scope, and Docstrings

Day 10 – Exception Handling and Debugging

Day 1 – Python Foundations for AEC Professionals