string manipulation
substring search
last occurrence index
programming
coding tips

Find index of last occurrence of a substring in a string

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Finding the last position of a substring is a common parsing task when you need the rightmost separator, suffix marker, or repeated token. Most languages provide a built-in method for this, and using it is usually clearer and faster than writing a manual loop.

Use the Language Primitive First

In Python, the standard tool is str.rfind(). It searches from right to left and returns the starting index of the last match. If the substring is missing, it returns -1.

python
text = "logs/app/server.log"
index = text.rfind("/")
print(index)

Output:

text
8

That index is useful because you can slice the string without extra parsing logic.

python
1text = "logs/app/server.log"
2slash = text.rfind("/")
3filename = text[slash + 1:] if slash != -1 else text
4print(filename)

rfind() Versus rindex()

Python also offers rindex(), which behaves similarly but raises ValueError when the substring does not exist.

python
text = "release-2026-03"
print(text.rfind("-"))
print(text.rindex("-"))

Choose between them based on control flow:

  • use rfind() when “not found” is an expected case
  • use rindex() when the substring must exist and missing data should fail loudly

For most application code, rfind() is easier to work with because you can branch on -1.

Searching Within a Range

Both methods accept optional start and end positions. That matters when you need the last match in only part of the string.

python
text = "2026-03-07-report.txt"
index = text.rfind("-", 0, 10)
print(index)

This searches only within the first ten characters. Range-limited searches are useful when you parse structured filenames, log prefixes, or version identifiers that contain multiple separators.

A Real Parsing Example

A frequent use case is extracting a file extension. Using the last dot is safer than splitting on every dot because filenames can contain multiple dots.

python
1filename = "backup.database.tar.gz"
2dot = filename.rfind(".")
3
4if dot == -1:
5    extension = ""
6    stem = filename
7else:
8    extension = filename[dot + 1:]
9    stem = filename[:dot]
10
11print(stem)
12print(extension)

Output:

text
backup.database.tar
gz

That gives you the final extension rather than the first one.

Handling Case Sensitivity

String search is case-sensitive by default. If you want a case-insensitive last match, normalize both strings first.

python
1text = "Error INFO error Warning"
2needle = "error"
3index = text.lower().rfind(needle.lower())
4print(index)

That works well for user input, log processing, and simple report generation. If locale-specific rules matter, you may need more specialized handling, but lowercasing is fine for many technical strings.

When the Substring Has More Than One Character

The method returns the index where the matching substring starts, not where it ends.

python
1text = "abc--marker--done"
2index = text.rfind("--")
3print(index)
4print(text[index:])

Output:

text
11
--done

This is especially important when you later slice the string. To move past the matched substring, add len(needle).

Alternatives in Other Languages

The concept is the same across ecosystems. JavaScript uses lastIndexOf, and Java uses lastIndexOf as well.

javascript
const text = "api/v1/users/42";
const index = text.lastIndexOf("/");
console.log(index);

Those built-ins are still preferable to manually scanning characters unless you are implementing a custom parser for a specialized format.

A Simple Helper Function

If you perform the same pattern repeatedly, wrap it in a small helper.

python
1def split_on_last(text: str, needle: str):
2    index = text.rfind(needle)
3    if index == -1:
4        return text, ""
5    return text[:index], text[index + len(needle):]
6
7left, right = split_on_last("[email protected]", "@")
8print(left)
9print(right)

This keeps calling code clean and centralizes the not-found behavior in one place.

Common Pitfalls

A common mistake is forgetting that rfind() returns -1 rather than throwing an exception. If you use that value directly in slicing without checking it, you may get incorrect output. Another mistake is assuming the result points to the end of the substring instead of its starting position. Developers also sometimes split the whole string into a list just to get the last separator, which is usually less direct than using rfind().

Summary

  • Use rfind() to get the last substring index without raising an exception.
  • Use rindex() when missing data should raise an error.
  • Check for -1 before slicing.
  • Remember the returned index is the start of the match.
  • Built-in right-to-left search methods are usually clearer than manual loops.

Course illustration
Course illustration

All Rights Reserved.