093. String Methods (Strip, Replace, Startswith, Endswith, Count)
Clean and analyze text data with string methods
093. String Methods (Strip, Replace, Startswith, Endswith, Count)
Rohanβs RAM manager reads process names from psutil. On some systems they come back as " chrome ", "CHROME", or "chrome (deleted)". He needs consistent names to group processes.
His first attempt:
def normalize_name(name):
name = name.strip() # remove leading/trailing spaces
name = name.lower() # lowercase
# BUG: split(" ") doesn't handle multiple spaces
name = " ".join(name.split(" "))
return name"chrome helper" still comes out as "chrome helper" β two spaces survive because split(" ") keeps empty strings between them.
The fix:
def normalize_name(name):
return " ".join(name.strip().lower().split())
# split() with no args splits on ANY whitespace and drops emptiesHe also needs to filter out kernel threads (names starting with "[") and redact sensitive process arguments:
def is_user_process(name):
return not name.startswith("[") # kernel threads are like "[kworker/0:0]"
def redact_args(cmdline, secret_flag):
return cmdline.replace(secret_flag, "[REDACTED]")
# replace() with no count replaces ALL occurrencesStrings are immutable. Every string method returns a NEW string.
name.strip()does not changenameβ you must capture the return value.
π‘ Fun fact: Python strings are immutable by design β once created, their bytes cannot change. This makes them safe to use as dictionary keys and to share across threads without locks. Languages like Java also make strings immutable for the same reasons. The trade-off is that s = s + "x" in a loop creates a new string object every iteration β for heavy string building, "".join(parts) is dramatically faster.
β οΈ Watch out: The most common string method mistake is forgetting to capture the return value β writing name.strip() on its own line and expecting name to be modified. Since strings are immutable, you must write name = name.strip(). This bites beginners constantly because mutable methods like list.append() work the opposite way.
π€ Think about it: split() with no arguments and split(" ") produce different results for "too many spaces". The no-arg version handles any whitespace and drops empty strings; the space version does not. Why does Python have two behaviors rather than one consistent approach, and when would you actually want split(" ") over split()?
Learning objectives
- Use strip() to remove whitespace from both ends
- Use split() (no args) vs split(β β) for robust word splitting
- Use replace() to substitute all occurrences
- Use endswith/startswith for suffix/prefix checks
Key concepts
- strip
- replace
- split
- startswith
- endswith
Try it
Concept detail
Key string methods β all return NEW strings (strings are immutable):
s.strip() β remove leading/trailing whitespace. s.strip(chars) β remove specific chars from both ends. s.lstrip() / rstrip() β strip from one side only.
s.lower() / s.upper() β case conversion.
s.replace(old, new) β replace ALL occurrences. s.replace(old, new, count) β replace only first count occurrences.
s.split() β split on any whitespace, drops empty strings (robust). s.split(β β) β split on a literal space β keeps empty strings between double spaces. s.split(β,β) β split on comma.
s.startswith(prefix) β True if s begins with prefix. s.endswith(suffix) β True if s ends with suffix. Both accept a tuple: s.endswith((β.comβ, β.orgβ, β.netβ))
s.count(sub) β count non-overlapping occurrences of sub. s.find(sub) β index of first match, -1 if not found. s.index(sub) β like find() but raises ValueError if not found.
Collapse multiple spaces idiom: β β.join(s.split()) β split on whitespace, rejoin with single space
Solution
def clean_input(s):
return " ".join(s.strip().lower().split())
def is_email(s):
return "@" in s and (s.endswith(".com") or s.endswith(".org") or s.endswith(".net"))
def count_words(text):
return len(text.split())
def redact(text, word):
return text.replace(word, "[REDACTED]")Tests
def test_clean_basic():
assert clean_input(" Hello World ") == "hello world"
def test_clean_multiple_spaces():
assert clean_input("too many spaces") == "too many spaces"
def test_is_email_valid():
assert is_email("[email protected]") == True
assert is_email("[email protected]") == True
def test_is_email_invalid():
assert is_email("notanemail") == False
assert is_email("@missingdomain.xyz") == False
def test_count_words():
assert count_words("hello world") == 2
assert count_words(" extra spaces ") == 2
def test_redact_all():
result = redact("bob stole from bob", "bob")
assert result == "[REDACTED] stole from [REDACTED]"