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