Python String Searching

In the last lesson you learned String Formatting. There you built clean text to show to people. Now flip it around. Sometimes you are not building text. You are looking inside text that already exists. Does this email have an @ in it? Does this filename end in .py? Where does the word “error” show up in this log line? That is what string searching is all about.

🤔 Why Search Inside a String?

Here is the pain. You have a chunk of text. You need to know something about it. Is a word in there? Where is it? How many times does it appear? Doing that by hand, one character at a time, would be slow. It would also be full of bugs.

Python gives you a small set of tools. They answer these questions in one line. You ask a question. Python gives you a straight answer. That is the whole idea.

These are the kinds of questions you can answer:

  • Is this piece of text inside that bigger text? (yes or no)
  • If it is there, at what position does it start?
  • Does the text start with something, or end with something?
  • How many times does a piece show up?

Let’s go through each tool one at a time.

🔍 The in Operator: A Quick Yes or No

Sometimes all you want to know is “is this thing inside that thing?”. For that, reach for in. It gives you back True or False, nothing else. Think of it like asking a friend “is there milk in the fridge?”. You do not want the exact shelf. You just want yes or no.

Here is the simplest check. We ask whether the small word sits inside the bigger sentence.

sentence = "I love learning Python"
print("Python" in sentence)
print("Java" in sentence)

Output

True
False

The word "Python" is in the sentence, so you get True. The word "Java" is not, so you get False. Simple.

Because in gives you True or False, it fits perfectly inside an if. This is where you will use it most of the time.

email = "alex@example.com"
if "@" in email:
print("Looks like an email")
else:
print("That is missing an @ sign")

Output

Looks like an email

Note

in is case sensitive. "python" in "I love Python" is False because the small p does not match the capital P. To ignore case, lower both sides first: "python" in sentence.lower().

📍 find(): Where Does It Start?

Sometimes yes or no is not enough. You want to know where the text sits. That is the job of find(). It hands you back the position where the match begins, counting from 0. And if the text is not there at all, it gives you -1.

Remember, Python counts positions starting at 0, not 1. So the first character is at position 0.

Here we ask where the word “love” begins inside the sentence.

sentence = "I love Python"
print(sentence.find("love"))
print(sentence.find("Python"))
print(sentence.find("Java"))

Output

2
7
-1

Let’s walk through that:

  • "love" starts at position 2. The I is 0, the space is 1, and the l is 2.
  • "Python" starts at position 7.
  • "Java" is not in the sentence at all, so find() returns -1.

That -1 is the signal for “not found”. So a common pattern is to check the result against -1.

log_line = "user logged in successfully"
position = log_line.find("error")
if position == -1:
print("No error in this line")
else:
print("Error found at position", position)

Output

No error in this line

⚠️ index(): Like find(), But It Errors

There is a method very similar to find() called index(). It does almost the same thing. It gives you the position where the text starts. The one big difference is what happens when the text is not there.

  • find() returns -1 when the text is missing. Calm, no fuss.
  • index() raises an error (a ValueError) when the text is missing. It stops your program.

Watch the difference. First, when the text is there, both behave the same.

name = "Riya Sharma"
print(name.find("Sharma"))
print(name.index("Sharma"))

Output

5
5

Now the missing case. With index(), asking for something that is not there throws an error.

name = "Riya Sharma"
print(name.index("Patel"))

Output

Traceback (most recent call last):
File "main.py", line 3, in <module>
print(name.index("Patel"))
~~~~~~~~~~^^^^^^^^^^
ValueError: substring not found

So which one should you pick? Here is the simple rule:

  • Use find() when the text might be missing and that is okay. You check for -1 and move on.
  • Use index() when the text should be there, and a missing value means something went genuinely wrong. The error then stops you and tells you about the problem.

Tip

If you are unsure, reach for find(). Getting back -1 is easier to handle than catching an error, and it keeps your program running.

Here is the difference side by side in a small table.

Situation find() index()
Text is found Returns the position Returns the position
Text is missing Returns -1 Raises a ValueError

🎬 startswith() and endswith(): Checking the Ends

A lot of the time you do not care where text sits in the middle. You only care about the start or the end. Does this web address begin with https? Does this file end with .py? For that you have startswith() and endswith(). Both give you back True or False.

Here we check both ends of a filename.

filename = "report.py"
print(filename.startswith("report"))
print(filename.endswith(".py"))
print(filename.endswith(".txt"))

Output

True
True
False

The filename does start with "report", so True. It does end with ".py", so True. It does not end with ".txt", so False.

This is genuinely useful in real code. Say you want to react only to Python files.

filename = "main.py"
if filename.endswith(".py"):
print("This is a Python file")
else:
print("Not a Python file")

Output

This is a Python file

Here is a nice extra. You can pass a tuple of choices, and it returns True if any of them match. That saves you from writing the check over and over.

filename = "photo.jpg"
if filename.endswith((".jpg", ".png", ".gif")):
print("This is an image file")

Output

This is an image file

🔢 count(): How Many Times?

The last tool answers “how many?”. count() tells you how many times a piece of text shows up inside a bigger text. If it is not there at all, you get back 0.

Here we count how many times the letter “a” appears, and how many times a whole pair of letters appears.

phrase = "banana"
print(phrase.count("a"))
print(phrase.count("na"))
print(phrase.count("z"))

Output

3
2
0

Let’s walk through it:

  • "a" shows up 3 times in “banana”.
  • "na" shows up 2 times.
  • "z" is not there, so you get 0.

One thing to keep in mind. count() counts matches that do not overlap. So counting "aa" inside "aaaa" gives you 2, not 3. Once a pair is counted, Python moves past it.

text = "aaaa"
print(text.count("aa"))

Output

2

⚠️ Common Mistakes

A few traps catch people again and again. Keep these in mind.

  • Forgetting that searches are case sensitive. "hello" and "Hello" are different. Lower both sides if you want a match that ignores case.
# ❌ Avoid: this is False because of the capital H
print("hello" in "Hello there")
# ✅ Good: lower the text first so case does not matter
print("hello" in "Hello there".lower())
  • Treating find()’s -1 as if it meant “found at the end”. It does not. -1 means not found. Always compare against -1.
# ❌ Avoid: this runs even when the text is missing, because -1 is truthy
if "error".find("x"):
print("found")
# ✅ Good: check the position properly
if "error".find("x") != -1:
print("found")
  • Reaching for index() on text that might be missing, then being surprised when the program stops with a ValueError. If it might be missing, use find().

✅ Best Practices

Here are some small habits that keep this clean.

  • Use in when you only need yes or no. It reads clearly and that is its whole purpose.
  • Use find() over index() unless a missing value truly is an error worth stopping for.
  • When you do not care about case, call .lower() on both sides before you search.
  • Reach for startswith() and endswith() instead of slicing the string yourself. They are clearer, and you will not get the positions wrong.

🧩 What You’ve Learned

Here is a quick recap of the tools you now have for looking inside text:

  • ✅ The in operator gives a quick True or False for “is this inside that?”.
  • find() returns the starting position, or -1 when the text is missing.
  • index() works like find() but raises a ValueError when the text is missing.
  • startswith() and endswith() check the start and end, and accept a tuple of choices.
  • count() tells you how many non-overlapping times a piece appears, or 0 if none.
  • ✅ All of these searches are case sensitive, so lower both sides when case should not matter.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does sentence.find("Java") return when "Java" is not in the sentence?

    Why: find() returns -1 when the text is not found, which is the signal you check against.

  2. 2

    How is index() different from find()?

    Why: Both return the starting position when found, but index() raises a ValueError when the text is missing while find() returns -1.

  3. 3

    What does "banana".count("a") return?

    Why: The letter "a" appears three times in "banana", so count() returns 3.

  4. 4

    Which check is the cleanest way to confirm a filename ends with .py?

    Why: endswith(".py") returns a clear True or False for whether the text ends with .py, which is exactly what you want.

🚀 What’s Next?

Now you can find things inside text. The next step is changing what you find. You swap one piece of text for another.

String Replacement

Share & Connect