Back to Blog
Python

Using Python String rindex to Find the Last Occurrence

Python's `str.rindex()` finds the last occurrence of a substring and returns its starting index. This guide covers syntax, practical examples, error handling, and how `rindex` compares with `rfind`.

Pythonstring methodsrindexrfindsubstring search
Illustration of Python string rindex scanning from the end to find the last occurrence of a substring.

Python's str.rindex() method finds the last occurrence of a substring and returns the index where that occurrence starts. It behaves like str.index() but searches from the end of the string rather than the beginning. This is useful when you want to locate the final delimiter, such as the dot before a file extension or the last slash in a URL.

The method is called on a string and takes the substring to search for as its first argument. It returns the highest index where the substring appears, which is the start of the last occurrence. If the substring is not present, it raises a ValueError.

How rindex Handles Parameters and Return Values

str.rindex(sub[, start[, end]]) accepts up to three arguments. The required sub is the substring to search for. The optional start and end define the slice of the original string to search within, using the same semantics as slicing. The search is performed only on that slice, but the returned index is still relative to the full string, not the slice.

For example:

text = 'the quick brown fox jumps over the lazy dog' last_the = text.rindex('the') print(last_the) # 31

Here, rindex scans from the end and finds the 'the' that starts at index 31. If you provide start and end, the search is restricted to that range. This is helpful when you want to ignore occurrences after a certain point.

text = 'apple, banana, cherry, banana, date' last_banana = text.rindex('banana', 0, 20) print(last_banana) # 7

In this case, the search is limited to the first 20 characters. The second 'banana' starts at index 23, so it is outside the range, and the method returns the starting index of the first 'banana', which is 7.

Practical Examples for Common Tasks

A frequent use case is extracting the file extension from a path. Since the last dot usually separates the extension, rindex is a natural fit:

filename = 'report.final.v2.pdf' dot_index = filename.rindex('.') extension = filename[dot_index + 1:] print(extension) # pdf

Similarly, you can get the last path component of a URL:

url = 'https://example.com/blog/latest-news' last_slash = url.rindex('/') slug = url[last_slash + 1:] print(slug) # latest-news

These examples show that rindex is more direct than manually reversing the string or using a loop when you need the position of the final occurrence.

Handling ValueError When the Substring Is Missing

If the substring is not found, rindex raises a ValueError. This is different from rfind, which returns -1. You need to handle this exception when the presence of the substring is not guaranteed. A typical pattern is:

text = 'hello world' try: pos = text.rindex('x') except ValueError: pos = -1

Alternatively, you can check first with in:

if 'x' in text: pos = text.rindex('x') else: pos = -1

The choice depends on whether you prefer exception handling or a conditional check. In most applications, the performance difference is negligible.

Comparing rindex and rfind

str.rfind() is the companion method that does not raise an exception; it returns -1 when the substring is absent. The choice between rindex and rfind often comes down to whether a missing substring is an expected condition or an error. If the absence is a normal part of the flow, rfind avoids the try/except boilerplate. If the absence indicates a bug or invalid input, rindex forces you to handle it explicitly.

MethodWhen the substring is missing
rindexRaises ValueError
rfindReturns -1

For example, when parsing user input where a delimiter might be optional, rfind is more convenient:

data = 'key=value' eq = data.rfind('=') if eq != -1: key = data[:eq] value = data[eq+1:]

If you used rindex here, you would need a try/except even though the missing delimiter is a valid case.

Performance and Memory Behavior

rindex searches the string from right to left, comparing characters until it finds a match or exhausts the search range. It does not create a copy of the string or use regular expressions, so its memory overhead is constant. The time complexity is O(n) in the length of the searched region, similar to index and find. For most strings this is not a concern, but if you are searching a very large text repeatedly, consider whether a different data structure, such as a suffix array or a precomputed index, would be more appropriate.

The optional start and end parameters can reduce the search space. If you know the substring cannot appear before a certain position, set start to that index to avoid scanning the earlier part of the string. This is a simple optimization that does not change the asymptotic complexity but can reduce the work involved.

Edge Cases and Compatibility Notes

rindex works with any substring, including the empty string. An empty substring is considered to be present at every position, so 'hello'.rindex('') returns the length of the string (5 in this case). This is consistent with the behavior of index and find.

For Python 3 str objects, rindex returns a character index, not a byte index. This is important when working with multi-byte characters. For example, 'café'.rindex('é') returns 3, not 4, because the string has four characters.

When using rindex with overlapping substrings, it returns the start of the last occurrence. For example, 'ababa'.rindex('aba') returns 2, because the substring 'aba' appears at index 0 and 2, and the last one starts at 2. The search does not skip overlapping matches; it simply scans from the end and returns the first match it finds, which is the one with the highest start index.

Python String rindex: Usage, Examples, and Comparison With rfind | RYUSLOG DEV