Skip to content

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:

contains.py
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)
True
False
False
True

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:

contains_if.py
email = input("Your email: ")

if "@" in email:
    print("That looks like an email address.")
else:
    print("An email address needs an @ sign.")
Your email: ada@example.com
That looks like an email address.

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:

starts_ends.py
filename = "holiday_photo.jpg"
url = "https://python.org"

print(filename.endswith(".jpg"))
print(filename.endswith(".png"))
print(url.startswith("https://"))
True
False
True

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:

count_find.py
sentence = "the cat sat on the mat"

print(sentence.count("the"))
print(sentence.count("at"))
print(sentence.find("cat"))
print(sentence.find("dog"))
2
3
4
-1

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, in is 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:

is_methods.py
print("2024".isdigit())
print("20.5".isdigit())
print("hello".isalpha())
print("hello world".isalpha())
print("   ".isspace())
True
False
True
False
True
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:

safe_input.py
text = input("How many tickets? ")

if text.isdigit():
    tickets = int(text)
    print(f"Booking {tickets} tickets.")
else:
    print("Please type a whole number.")
How many tickets? three
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_checker.py
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.")
Choose a password: hunter2
Too short. Use at least 8 characters.
Choose a password: Tr0ub4dor
Strong password.

Read the elif chain carefully. Each branch is a rule, and the rules are checked top to bottom:

  1. len(password) < 8: too short.
  2. password.isalpha(): only letters, so there is no number.
  3. password.isdigit(): only digits, so there is no letter.
  4. password == password.lower(): lowercasing changed nothing, so there was no capital letter. A neat trick.
  5. " " 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 ' requires string as left operand, not int

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

  1. Yes or no. Ask "Do you want to continue?" and accept yes or no in any capitalization, with or without spaces around it. Print a different message for yes, no, and anything else.
  2. 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 .com or .org. Otherwise print "Email accepted."
  3. 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
answer = input("Do you want to continue? (yes/no) ").strip().lower()

if answer == "yes":
    print("Continuing.")
elif answer == "no":
    print("Stopping.")
else:
    print("Please answer yes or no.")
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 text and "a" not in text test 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 -1 if 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.

Next: Repeating with for