Beginner16 min

String methods reference

Every string method that matters, grouped by task — cleaning, searching, splitting, and formatting.

Why this matters in AI / ML / GenAI

Text preprocessing is most of the work in any NLP or LLM pipeline. Prompt templates, log parsing, and cleaning scraped data are all string methods, and doing them correctly is faster and more readable than reaching for regex.

Cleaning and case

Strings are immutable — every method returns a new string and the original is unchanged. Forgetting to assign the result is the most common string bug: text.strip() on its own does nothing.

  • strip(), lstrip(), rstrip() — remove whitespace, or any characters you pass
  • lower(), upper(), title(), capitalize(), swapcase()
  • casefold() — aggressive lowercasing for cross-language comparison; prefer it over lower() when comparing user input
  • removeprefix(), removesuffix() — safer than slicing, since they do nothing when the affix is absent

Note that strip("abc") removes any of those characters from the ends, not the substring "abc". That trips people up.

Searching and testing

  • in — the simplest containment check, and usually the right one
  • find() returns -1 when missing; index() raises ValueError
  • startswith(), endswith() — both accept a tuple of options
  • count() — non-overlapping occurrences
  • replace(old, new, count) — the optional count limits replacements

Validation predicates: isdigit(), isalpha(), isalnum(), isspace(), islower(), isupper(), istitle().

Watch out: "3.14".isdigit() is False because of the dot, and "-5".isdigit() is False because of the sign. For numeric validation, try converting inside a try/except instead.

Splitting, joining, and formatting

split() with no argument splits on any run of whitespace and drops empties — usually what you want for text. split(",") splits on an exact delimiter and keeps empty fields. splitlines() handles all newline conventions.

"".join(parts) is the correct way to build a string from pieces. Repeated += in a loop creates a new string every iteration and is quadratic on large inputs.

partition(sep) returns exactly three parts (before, separator, after), which makes it cleaner than split for "key=value" parsing.

f-strings are the modern format: f"{value:.2f}", f"{n:,}", f"{text:>10}", f"{ratio:.1%}". Add = for debugging: f"{score=}" prints both the name and the value.

For alignment use ljust, rjust, center, or zfill for zero-padded numbers.

Copy-paste examples

Copy into your own editor, or load one into the compiler below and press Run.

Cleaning user and scraped text

Strings are immutable — you must assign the result.

raw = "   Hello, GenAI World!\t\n"

print(repr(raw))
print(repr(raw.strip()))
print(repr(raw.strip().lower()))

raw.strip()                      # result discarded — a very common bug
print("original unchanged:", repr(raw))

path = "logs/model-run.json"
print("\nremovesuffix:", path.removesuffix(".json"))
print("removeprefix:", path.removeprefix("logs/"))
print("missing affix is a no-op:", path.removesuffix(".csv"))

print("\nstrip('/-') strips characters, not the substring:")
print(repr("--/api/users/--".strip("/-")))

print("\ncasefold beats lower for comparison:", "STRASSE".casefold() == "strasse".casefold())

Searching and validating

find returns -1, index raises. isdigit is stricter than people expect.

text = "model=gpt-4.1-mini temperature=0.2 stream=true"

print("'temperature' in text ->", "temperature" in text)
print("find('stream')        ->", text.find("stream"))
print("find('missing')       ->", text.find("missing"), "(-1, no exception)")
print("count('=')            ->", text.count("="))
print("startswith tuple      ->", text.startswith(("model", "engine")))

print("\nvalidation predicates:")
for candidate in ["42", "3.14", "-5", "abc", "a1", "  "]:
    print(f"  {candidate!r:<8} isdigit={candidate.isdigit()!s:<6} "
          f"isalpha={candidate.isalpha()!s:<6} isalnum={candidate.isalnum()}")

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

print("\nreliable numeric check:", {s: is_number(s) for s in ["42", "3.14", "-5", "abc"]})

Splitting and joining

partition is cleaner than split for key=value pairs.

line = "model=gpt-4.1-mini,temp=0.2,stream=true"

settings = {}
for chunk in line.split(","):
    key, sep, value = chunk.partition("=")
    if sep:
        settings[key] = value
print(settings)

print("\nsplit() vs split(','):")
print("  ", "a  b   c".split())
print("  ", "a,,b".split(","), "(keeps the empty field)")
print("  ", "a,b,c".split(",", maxsplit=1), "(maxsplit)")

log = "line one\nline two\r\nline three"
print("\nsplitlines handles all newline styles:", log.splitlines())

parts = ["retrieve", "rerank", "generate"]
print("\njoined:", " -> ".join(parts))
print("csv row:", ",".join(str(x) for x in [1, 2.5, "text"]))

f-string formatting specifiers

The specifiers you will reuse constantly in reports and logs.

score = 0.9137
tokens = 1234567
name = "mini"

print(f"2 decimals   : {score:.2f}")
print(f"percent      : {score:.1%}")
print(f"thousands    : {tokens:,}")
print(f"scientific   : {tokens:.2e}")
print(f"plus sign    : {score:+.3f}")
print(f"pad left     : |{name:>10}|")
print(f"pad right    : |{name:<10}|")
print(f"centred      : |{name:^10}|")
print(f"fill char    : |{name:*^10}|")
print(f"zero padded  : {7:03d}")

print(f"\ndebug form   : {score=}")
print(f"repr in field: {name!r}")

width = 8
print(f"dynamic width: |{name:>{width}}|")

print("\nold ways still work:")
print("percent style: %.2f" % score)
print("str.format   : {:.2f}".format(score))

Building an aligned report

ljust, rjust, and zfill for table output without a library.

rows = [("gpt-4.1-mini", 0.913, 620), ("llama-3-8b", 0.847, 1520), ("claude-haiku", 0.900, 460)]

header = f"{'model'.ljust(16)}{'accuracy'.rjust(10)}{'tokens'.rjust(9)}"
print(header)
print("-" * len(header))
for model, acc, tokens in rows:
    print(f"{model.ljust(16)}{f'{acc:.3f}'.rjust(10)}{f'{tokens:,}'.rjust(9)}")

print("\nzfill for ids:", [str(i).zfill(4) for i in [1, 42, 999]])
print("centred title:", "SUMMARY".center(35, "="))
print("wrapped label:", "retrieval".upper().ljust(20, "."), "OK")

Clean and parse a messy log line

Try it — in-browser Python

Add another key=value pair to the raw line and it parses automatically.

Output

Python runs in your browser. First run downloads the runtime.

Press Run (or Ctrl+Enter) to execute.

CPython in WebAssembly. Stdlib works. NumPy, pandas, scikit-learn and Matplotlib load on demand, and charts render below. No input(), no GPU, no network installs.

Takeaways

  • Strings are immutable — always assign the result of a method call.
  • Use join to build strings, partition for key=value, and split() bare for whitespace.
  • f-string specifiers (.2f, :,, :.1%, alignment) cover almost all formatting needs.