Skip to main content
PROGRAMMING / PYTHON / LESSON 04

Practical String Processing.

Index, slice, search, normalize and transform text. Practice the small algorithms that turn everyday messages into useful program logic.

Beginner65–85 minutesInteractive explorerReal Python workbench
00

What you'll learn

You've learned to make decisions and repeat work. Now apply that control flow directly to text: find a symbol in an address, count vowels, extract part of a ticket ID and inspect fictional log lines.

  • Use len(), zero-based indices and negative indices to inspect text safely.
  • Slice strings with start, stop and step, including reverse slices.
  • Use in and not in for substring membership and case-insensitive checks.
  • Distinguish find(), index() and count() when locating text.
  • Identify prefixes and suffixes with startswith() and endswith().
  • Clean and combine text with strip(), lower(), upper(), replace(), split() and join().
  • Write a character-by-character loop that counts matching letters.
  • Build a small independent text-processing script and test varied inputs.
Learning sequence: Read the concept, predict the code's output, experiment with the visual explorer, then write the real Python. Use the optional local Python setup from Lesson 01 to practice the same snippets on your computer.
TEXT HAS POSITIONS
01

A string is a sequence of characters

Python sees text as a sequence, not a mysterious single object. len(text) reports the number of characters, with spaces and punctuation included. Positions begin at zero: name[0] is the first character and name[-1] is the last. Indexing an empty string or an out-of-range position raises IndexError. This is different from slicing, which safely returns shorter strings when a boundary goes beyond the text.

Zero-based indexing

'SW-01'[0] is 'S'; 'SW-01'[1] is 'W'. The index counts characters, not words.

Negative positions

'SW-01'[-1] returns '1', the last character. Before indexing a potentially empty value, check if text: or len(text) > 0.

PYTHON / PREDICT THE RESULTRead before you run
device = 'SW-01'
print(len(device))
print(device[0])
print(device[-1])
What values print, and what would device[5] do? Reveal the explanation after you predict.

The lines print 5, S, and 1. There are five characters with indices 0 through 4; index 5 would raise IndexError.

INTERACTIVE STRING EXPLORER / SIMULATED PYTHON

Every character has a position.

Change the fictional message to see its indexes, slices and searches. Use the real Python editor below to test your understanding.

0S
1W
2-
30
41
5,
6·
7O
8f
9f
10l
11i
12n
13e
SLICEtext[0:5]"SW-01"
LAST CHARACTERtext[-1]"e"
REVERSEtext[::-1]"enilffO ,10-WS"
MEMBERSHIPneedle in text.lower()true
FIRST INDEXtext.lower().find(needle)7
OCCURRENCEStext.lower().count(needle)1

Search converts both values to lowercase. This explorer treats an empty search field as no query; Python treats empty substrings differently. A dot (·) represents a space.

Remember: Text is a sequence. Start at index 0, use -1 for the last character, and guard against empty strings.
SELECT ONLY PART OF THE TEXT
02

Slicing: start, stop and step

A slice uses text[start:stop:step] to build a new string. Start is included, stop is excluded, and the step controls the movement. Omitting start or stop defaults to the relevant end; a negative step can reverse characters. Because strings are immutable, slicing creates a new string and never modifies the original. For beginners, first reason through a simple positive-range slice, then experiment with [-3:] and [::-1].

Stop is exclusive

'INC-2047'[0:3] is 'INC', not 'INC-'. The fourth character at index 3 is excluded.

Reverse with a negative step

'abc'[::-1] produces 'cba'. This is a useful way to practice how the slice step changes direction.

PYTHON / PREDICT THE RESULTRead before you run
ticket = 'INC-2047'
print(ticket[:3])
print(ticket[-4:])
print(ticket[::-1])
What do the first two slices produce, and does the original ticket change? Reveal the explanation after you predict.

The first is INC and the second is 2047. The reversed text is 7402-CNI. The original ticket remains unchanged.

Remember: Slicing includes start, excludes stop, and returns new text. Use [:], [-n:] and [::-1] intentionally.
ASK WHETHER TEXT CONTAINS TEXT
03

Search with in and not in

Membership testing is one of the most useful beginner programming skills. The expression '@' in address evaluates to True or False; it does not require a loop or complicated regular expression. not in expresses the opposite. Matching is case-sensitive, so normalize with lower() before case-insensitive checks. One symbol is not enough to validate an email address: for now, we are only checking whether a marker exists.

Boolean answer, not a position

'@' in 'student@example.com' returns True. A similar-looking string without @ returns False.

Use lower() for case-insensitive text

'error' in 'ERROR: timeout'.lower() is True. Without lower(), the case mismatch makes the expression False.

PYTHON / PREDICT THE RESULTRead before you run
email = 'student@example.com'
message = 'ERROR: timeout'
print('@' in email)
print('error' in message.lower())
print('offline' not in message.lower())
Why are all three results True? Does @ alone prove the address is deliverable? Reveal the explanation after you predict.

The address contains @, the message becomes lowercase and contains error, and offline is absent. All expressions are True. An @ symbol alone does not establish a valid or deliverable email address.

Remember: Use in and not in to test substring presence; normalize case when the requirement is case-insensitive.
FIND POSITIONS AND OCCURRENCES
04

find(), index() and count() aren't interchangeable

Sometimes you need the location of a substring instead of a Boolean membership result. text.find(part) returns the first start index, or -1 if absent. text.index(part) is similar but raises ValueError when no match exists. text.count(part) counts non-overlapping occurrences. Choose based on whether an absent match is expected or exceptional. A simple membership test is clearer when you only need True or False.

Missing is -1 with find()

'port 443'.find('443') returns 5; finding '22' returns -1. Do not confuse that -1 result with the last character index.

Count repeated markers

'error,error,warning'.count('error') is 2. It counts substring occurrences rather than unique log events.

PYTHON / PREDICT THE RESULTRead before you run
message = 'error|warning|error'
print(message.find('warning'))
print(message.find('offline'))
print(message.count('error'))
Which result means 'not found', and why is that different from index()? Reveal the explanation after you predict.

The results are 6, -1, and 2. find returns -1 when absent, whereas index would raise ValueError. count returns the number of matching substrings.

Remember: Use in for presence, find for a position or -1, count for occurrences, and index only when absence should be an error.
RECOGNIZE STRUCTURE
05

startswith() and endswith() for predictable patterns

Filenames, ticket identifiers, and log lines often have known beginnings and endings. startswith(prefix) and endswith(suffix) return Booleans. They avoid hand-counting positions for simple prefix/suffix tests. They are case-sensitive and do not validate every detail of a filename; combine with strip() or lower() when the problem requires normalization.

Prefix

'WARN AP-02 offline'.startswith('WARN') is True, useful when separating warnings from info messages.

Suffix

'events.log'.endswith('.log') is True. A filename ending in .log does not prove its contents are trustworthy or actually log data.

PYTHON / PREDICT THE RESULTRead before you run
filename = '2026-11-08-alert.log'
print(filename.startswith('2026'))
print(filename.endswith('.log'))
print(filename.endswith('.csv'))
Why is the final expression False, even though filename contains other periods or dashes? Reveal the explanation after you predict.

endswith checks the actual final characters. The filename ends in .log rather than .csv, so the last result is False.

Remember: Use startswith/endswith for simple prefix and suffix rules, not fragile manual position checks.
CLEAN BEFORE COMPARING
06

strip(), lower(), upper(), and replace()

Text from exports or terminals may contain leading spaces, inconsistent capitalization or separators. strip() removes surrounding whitespace; lower() and upper() return transformed versions; replace(old, new) returns a new string with matching substrings replaced. None of these methods changes the original string, because Python strings are immutable. Be deliberate with replacements: replacing every hyphen in a hostname could change its meaning.

Chain small transformations

' Offline '.strip().lower() becomes 'offline'. Read chained methods from left to right.

Immutability

normalized = raw.strip().upper() does not rewrite raw. Store the transformed result in a variable if the caller needs it.

PYTHON / PREDICT THE RESULTRead before you run
raw = '  ap-02 | OFFLINE  '
cleaned = raw.strip().replace(' | ', ': ').upper()
print(cleaned)
print(raw == cleaned)
What prints? Does replace alter raw in place? Reveal the explanation after you predict.

The first line is AP-02: OFFLINE and the second is False. The chained methods return new strings; raw still contains its original spacing and capitalization.

Remember: Clean text through deliberate transformations and assign their results; strings cannot be modified in place.
ONE LINE BECOMES MANY FIELDS
07

split() and join() transform a message

A line such as 'SW-01, offline, HQ' contains several fields separated by commas. split(',') produces a list of parts (including spaces around values), while strip() cleans each one. A delimiter's join(items) creates one string from a sequence of strings. A split is convenient for simple, controlled text formats; for real CSV with quoted commas, use csv.DictReader in the Files lesson.

Split produces a list

'a,b,c'.split(',') returns ['a', 'b', 'c']. You can assign fields by position if you know the exact format and number of fields.

Join expects text

' | '.join(['SW-01', 'offline']) produces 'SW-01 | offline'. To join numbers, convert them to strings first.

PYTHON / PREDICT THE RESULTRead before you run
raw = 'SW-01, offline, HQ'
parts = [item.strip() for item in raw.split(',')]
host, status, site = parts
print(f'{host} @ {site}: {status}')
Why do we strip each field after split? What would happen for a quoted field containing a comma? Reveal the explanation after you predict.

split(',') leaves spaces around fields, so strip cleans them. Splitting a real CSV quoted field with a comma would be incorrect; use the csv module for such files.

Remember: Use split and join for simple controlled text formats; use the csv module for actual CSV exports.
APPLY CONTROL FLOW TO STRINGS
08

Count vowels by visiting characters

A string itself is iterable. You can write for letter in message: to inspect each character in order. Convert the message to lowercase to avoid repeating uppercase comparisons, then use membership letter in 'aeiou'. Keep the counter outside the loop and increment it only for matches. This combines Control Flow with string processing and mirrors the kind of unfamiliar problem a beginner may see in an exam or work task.

Iterate through text directly

You don't need range(len(text)) just to visit each character. for letter in text is simpler and avoids indexing mistakes.

Test zero, one and many matches

Try 'sky' (0 vowels), 'A' (1), and 'Education' (5). The logic should work for each without hardcoding the word.

PYTHON / PREDICT THE RESULTRead before you run
message = 'Education'
vowel_count = 0
for letter in message.lower():
    if letter in 'aeiou':
        vowel_count += 1
print(vowel_count)
Why does the program print 5, and what would happen if we removed lower()? Reveal the explanation after you predict.

Education contains e, u, a, i, o—five vowels ignoring case. Removing lower() would cause the capital E not to match the lowercase vowel string, giving 4.

Remember: Use a loop and membership to build small text algorithms; validate against several input strings.
WRITE THE TEXT LOGIC YOURSELF
09

Make a small text-based troubleshooting report

Combine the techniques: scan fictional log lines, select those beginning with WARN, extract hostnames and return an alphabetized list of distinct hosts. This exercise introduces a little list handling from the earlier Control Flow lesson, which Working with Data will explore more thoroughly. Do not confuse the presence of a word in text with a validated operational fact. You're writing a local parser for a known fictional format, not examining real devices.

Construct the algorithm

Describe the steps in plain language before writing code: visit every line, choose warnings, extract a hostname, collect distinct hosts, and format a report.

Check changed input

A correct algorithm should behave when a warning repeats, when there are zero warnings, or when a different host appears.

PYTHON / PREDICT THE RESULTRead before you run
events = ['WARN AP-02 offline', 'INFO SW-01 up']
for event in events:
    if event.startswith('WARN '):
        parts = event.split()
        print(parts[1])
What does this print, and why does the other line not appear? Reveal the explanation after you predict.

It prints AP-02. Only the first event begins with WARN, so the INFO line is skipped. A later exercise extends this to multiple unique sorted hostnames.

Remember: String searching and control flow let you inspect text and turn a plain-English requirement into code.
APPLY IT YOURSELF
10

Inspect a string in real Python

The lesson begins with a guided exercise; complete the full coding clinic afterward. Edit real Python, read the console and trace, then check the result with the given inputs. Change the inputs and test other cases too.

JOELEARNS / PYTHON WORKBENCH

Write it. Run it. Understand it.

Learn character indexing, string searching, slicing and original text algorithms in real Python.

Python 3.14 · in-browser
CHALLENGE 01 / 9 · BEGINNER

Inspect the first and last characters

Read the supplied device name using input('Device: '), store its first character in first and its last character in last, and print exactly 'SW-01: S / 1'. Do not hardcode the characters; use indexing so changing the hostname changes the result.

EXPECTED OUTPUTDevice: SW-01 SW-01: S / 1
script.py editableCtrl / Cmd + Enter to run
Responses for input() — only when your script asks

What this does: If your code asks input("Device? ") and then input("Port? "), Python reads your first line as the device and the second as the port. Run uses the responses above. Check uses the exercise's provided test inputs. The result of input() is always text until you convert it. If your code never calls input(), you don't need this panel.

Your code is stored only in session storage when enabled. Never paste secrets. Clearing this option clears saved drafts, not the editor.
Code executes only when you choose Run or Check. No account needed.
HINTS · OPTIONAL

Try solving it before asking for help. Hints never show a completed program.

PYTHON OUTPUTReady

Your printed output and errors will appear here.

VARIABLE TRACER

Run the code, then step through the executed lines to watch variable values change.

About this environment: The Python runtime is loaded on demand from a version-pinned CDN and runs inside a sandboxed, opaque-origin browser frame. Runs have a time limit. OS/network automation is outside this introductory workbench. If your code calls input(), a response panel appears to supply text to those calls. Don't paste secrets or untrusted scripts.

Next required step: complete the 9-challenge Practice milestone →
NEXT STEP / PRACTICE

Move from recognition to independent coding.

Some tasks fix a short script; later tasks require you to write the algorithm yourself. Optional hints provide direction without revealing the finished program.

Continue to all Practical String Processing challenges →After Practice: Working with Data →
11

Official references

Use the official Python documentation to confirm exact syntax or behavior. The browser workbench and the local Python interpreter should agree for these basic operations.

NEXT REQUIRED STEP · PYTHON FUNDAMENTALS

Complete the Practice milestone

You've reached the end of this written lesson. Your next step is practice—complete Practical String Processing Coding Challenges before proceeding to a new lesson. This is milestone 8 of 18. You can revisit the explanation whenever you need it.