Searching and checking text¶
Goal: test what a string contains, and use those tests to check the user's input before trusting it.
Why this matters¶
Users type all sorts of things: letters where you wanted a number, a name with a typo, an empty line. A good program checks input before using it. This lesson gives you the tools, then puts them together in your first mini project: a password strength checker.
Is it in there?¶
The in keyword asks whether one string appears inside another:
message = "Meet me at the station at 6pm"
print("station" in message)
print("airport" in message)
print("Station" in message)
print("airport" not in message)
Two things to notice. First, in is case-sensitive: "Station" with a capital S is not found.
Second, not in is the opposite test, and it reads like plain English.
Because in produces a boolean, it slots straight into an if:
email = input("Your email: ")
if "@" in email:
print("That looks like an email address.")
else:
print("An email address needs an @ sign.")
Case-insensitive search
To find "station" no matter how it is capitalized, lowercase both sides:
"station" in message.lower().
Beginning and end¶
.startswith() and .endswith() check the edges of a string. They are handy for file names and web addresses:
filename = "holiday_photo.jpg"
url = "https://python.org"
print(filename.endswith(".jpg"))
print(filename.endswith(".png"))
print(url.startswith("https://"))
Counting and locating¶
.count() tells you how many times a piece of text appears. .find() tells you where it first appears,
as an index, the same kind of position number you used for slicing in the last lesson:
sentence = "the cat sat on the mat"
print(sentence.count("the"))
print(sentence.count("at"))
print(sentence.find("cat"))
print(sentence.find("dog"))
Two details worth remembering:
.count("at")found three: in cat, sat, and mat. It counts every occurrence, even inside other words..find("dog")returned-1. That is how.find()says "not found". It does not crash. If you only need to know whether something is present,inis clearer. Use.find()when you need the position.
What kind of text is it?¶
A family of methods with names starting is tests what a string is made of. Each returns True or False:
print("2024".isdigit())
print("20.5".isdigit())
print("hello".isalpha())
print("hello world".isalpha())
print(" ".isspace())
| Method | True when the string is |
|---|---|
.isdigit() |
all digits, 0 to 9, and not empty |
.isalpha() |
all letters, and not empty |
.isspace() |
all whitespace, and not empty |
Note the "20.5" case: the dot is not a digit, so .isdigit() is False. It only recognizes whole numbers.
Checking before converting¶
In Module 1, int(input(...)) crashed if the user typed letters. Now you can check first:
text = input("How many tickets? ")
if text.isdigit():
tickets = int(text)
print(f"Booking {tickets} tickets.")
else:
print("Please type a whole number.")
This is the pattern: check, then convert. You will see a more powerful way to handle bad input in the module on errors, but this one is simple and good enough for many programs.
Mini project: password strength checker¶
Time to combine everything from Module 1 and Module 2. The program below asks for a password and reports the first problem it finds, or declares the password strong:
password = input("Choose a password: ")
if len(password) < 8:
print("Too short. Use at least 8 characters.")
elif password.isalpha():
print("Add at least one number.")
elif password.isdigit():
print("Add at least one letter.")
elif password == password.lower():
print("Add at least one capital letter.")
elif " " in password:
print("Spaces are not allowed.")
else:
print("Strong password.")
Read the elif chain carefully. Each branch is a rule, and the rules are checked top to bottom:
len(password) < 8: too short.password.isalpha(): only letters, so there is no number.password.isdigit(): only digits, so there is no letter.password == password.lower(): lowercasing changed nothing, so there was no capital letter. A neat trick." " in password: contains a space.
Type it in, run it with a few passwords, then improve it. Ideas: require a punctuation mark, or reject a list of
common passwords such as "password123".
Try it¶
Ask the user for a sentence and tell them whether it is a question. A question ends with ?. Bonus: also report whether
it contains the word "python", in any capitalization.
Common mistakes¶
TypeError: 'in
You wrote 5 in "12345". Both sides of in must be strings here: "5" in "12345".
The check passes when it shouldn't
Often a capitalization problem. "yes" == "Yes" is False. Normalize input with .strip().lower() before comparing.
.isdigit() is False for a negative number
"-5".isdigit() is False because - is not a digit. For this course, treat that as expected; handling signs properly
comes later.
Exercises¶
- Yes or no. Ask "Do you want to continue?" and accept
yesornoin any capitalization, with or without spaces around it. Print a different message for yes, no, and anything else. - Email check. Ask for an email address and reject it if it has no
@, if it contains a space, or if it does not end in.comor.org. Otherwise print "Email accepted." - Word finder. Ask for a sentence and a word. Report whether the word appears, how many times, and the position of the first occurrence. Ignore capitalization.
Solution 1
Solution 2
email = input("Email address: ").strip()
if "@" not in email:
print("Missing the @ sign.")
elif " " in email:
print("Email addresses cannot contain spaces.")
elif not email.endswith(".com") and not email.endswith(".org"):
print("Only .com and .org addresses are accepted.")
else:
print("Email accepted.")
Rules are checked one at a time, so the message names the first problem found. not a and not b reads as
"neither ending matches".
Solution 3
sentence = input("Type a sentence: ")
word = input("Word to look for: ")
if word.lower() in sentence.lower():
position = sentence.lower().find(word.lower())
times = sentence.lower().count(word.lower())
print(f"Found '{word}' {times} time(s), first at position {position}.")
else:
print(f"'{word}' is not in the sentence.")
Both the sentence and the word are lowercased before every comparison, so the search ignores capitalization.
Summary¶
"a" in textand"a" not in texttest whether text contains something. They are case-sensitive..startswith()and.endswith()check the edges of a string..count()counts occurrences..find()gives the first index, or-1if absent..isdigit(),.isalpha(), and.isspace()test what a string is made of.- Check input before converting it:
if text.isdigit(): number = int(text).
Module 2 complete¶
You can now take text apart, clean it, and validate it. Module 3 unlocks the most powerful idea so far: loops, which let a program repeat work without you copying and pasting lines.