Python String Searching
Table of Contents + −
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
TrueFalseThe 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 emailNote
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
27-1Let’s walk through that:
"love"starts at position2. TheIis0, the space is1, and thelis2."Python"starts at position7."Java"is not in the sentence at all, sofind()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-1when the text is missing. Calm, no fuss.index()raises an error (aValueError) 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
55Now 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 foundSo 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-1and 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
TrueTrueFalseThe 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 fileHere 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
320Let’s walk through it:
"a"shows up3times in “banana”."na"shows up2times."z"is not there, so you get0.
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 Hprint("hello" in "Hello there")
# ✅ Good: lower the text first so case does not matterprint("hello" in "Hello there".lower())- Treating
find()’s-1as if it meant “found at the end”. It does not.-1means not found. Always compare against-1.
# ❌ Avoid: this runs even when the text is missing, because -1 is truthyif "error".find("x"): print("found")
# ✅ Good: check the position properlyif "error".find("x") != -1: print("found")- Reaching for
index()on text that might be missing, then being surprised when the program stops with aValueError. If it might be missing, usefind().
✅ Best Practices
Here are some small habits that keep this clean.
- Use
inwhen you only need yes or no. It reads clearly and that is its whole purpose. - Use
find()overindex()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()andendswith()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
inoperator gives a quickTrueorFalsefor “is this inside that?”. - ✅
find()returns the starting position, or-1when the text is missing. - ✅
index()works likefind()but raises aValueErrorwhen the text is missing. - ✅
startswith()andendswith()check the start and end, and accept a tuple of choices. - ✅
count()tells you how many non-overlapping times a piece appears, or0if 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
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
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
What does "banana".count("a") return?
Why: The letter "a" appears three times in "banana", so count() returns 3.
- 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.