Back to Blog
Python

Using Python string rfind for Right-Side Search

Learn how python string rfind searches from the right, its parameters, return values, and practical differences from find for real-world text processing.

string methodssubstring searchrfindPython standard librarytext processing
Illustration of a Python string with a search pointer moving from the right side to locate the last occurrence of a substring.

When you need to locate the last occurrence of a substring in a Python string, str.rfind is the standard method. The python string rfind method scans from the end of the string toward the beginning and returns the highest index where the substring is found. If no match exists, it returns -1. This behavior is distinct from str.find, which returns the lowest index from the left. Both methods share the same parameter structure, but their search direction makes them suitable for different tasks.

Syntax and Parameters

The full signature is:

str.rfind(sub[, start[, end]])
  • sub is the substring to search for. It can be any string, including an empty string.
  • start and end are optional integer indices that define the slice of the string to search within. The search is performed over str[start:end], but the returned index is relative to the original string, not the slice.

If start or end are omitted, the entire string is searched. Negative indices are allowed and follow the same slicing rules as Python sequences. For example, s.rfind('x', -5) starts searching five characters from the end of the string.

How rfind Searches from the Right

The core behavior is straightforward: the method starts at the end of the search range and moves backward until it finds a match. It does not check all possible starting positions in forward order, so it can stop early when it encounters the rightmost occurrence.

path = "/home/user/projects/main.py" last_slash = path.rfind("/") print(last_slash) # 17

Here, rfind returns the index of the slash before main.py, not the first slash. This makes it natural for extracting the final component of a path or a filename from a fully qualified name.

Because the search is right-to-left, the result is the largest index i such that s[i:i+len(sub)] == sub, within the bounds defined by start and end. If multiple overlapping matches exist, the one with the highest starting index wins.

Using start and end to Bound the Search

When you pass start and end, the method only considers the substring s[start:end]. The returned index is still an absolute position in the original string. This is useful when you want to ignore a known prefix or suffix.

text = "config: host=localhost; host=backup" first_section_end = text.find(";") last_host_in_first = text.rfind("host", 0, first_section_end) print(last_host_in_first) # 7

The search is limited to the part before the semicolon, so it finds the host in the first configuration block, not the later one. Without end, it would return the index of the second host.

Negative indices work as expected. s.rfind("x", -10, -1) searches the slice from len(s)-10 to len(s)-1. This can be handy when you only care about the tail of a string.

rfind vs find: When to Use Which

The choice between rfind and find depends on whether you need the first or last occurrence. The table below summarizes the key differences.

MethodSearch directionReturnsTypical use case
findLeft to rightLowest indexFirst occurrence, e.g., first delimiter
rfindRight to leftHighest indexLast occurrence, e.g., file extension separator

For example, to get the file extension from a path, rfind is the natural choice because you want the last dot, not the first one.

filename = "archive.tar.gz" ext_dot = filename.rfind(".") print(filename[ext_dot+1:]) # gz

Using find would incorrectly return tar as the extension. Conversely, when parsing a simple key-value pair where the first = matters, find is appropriate.

The methods are interchangeable in terms of performance for most strings because both are implemented in C and scan linearly. The difference is only in the direction and the early-exit condition. For a string where the match is near the end, rfind can stop sooner than a full forward scan, but the magnitude is negligible unless the string is very large and the match is extremely close to the end.

Handling Missing Substrings

Both rfind and find return -1 when the substring is not found. This is a sentinel value, not an exception. You must check for it explicitly before using the result as an index.

s = "hello world" idx = s.rfind("z") if idx == -1: print("not found") else: print(s[idx:])

A common mistake is to assume the result is always a valid index. Using -1 directly in slicing can produce surprising results because s[-1:] returns the last character, not an empty string. Always guard against -1 when the substring may be absent.

Performance and Runtime Behavior

The implementation of rfind is a linear scan over the search range in the worst case. It does not allocate additional memory beyond the input string, so memory usage is constant. The time complexity is O(n) where n is the length of the search range, assuming the substring length is small relative to the string. For pathological cases, such as a substring that is nearly as long as the string, the underlying algorithm may behave like a naive search, but CPython uses an optimized algorithm that is typically faster than a simple loop.

If you are performing many searches on the same string, consider whether you can precompute positions or use a more specialized data structure. For a single search, rfind is the simplest and most readable option. There is no need to write a manual loop that iterates from the end, because the built-in method is both clearer and less error-prone.

Common Usage Patterns in Real Code

One frequent pattern is extracting the last path component from a URL or filesystem path. rfind combined with slicing gives you the tail without importing os.path or pathlib when you only need a quick extraction.

url = "https://example.com/api/v2/users" last_slash = url.rfind("/") resource = url[last_slash+1:] if last_slash != -1 else url print(resource) # users

Another pattern is removing a known suffix. If you know the string ends with a particular suffix, you can verify with endswith and then use rfind to locate the start of that suffix.

log_line = "ERROR: disk full" if log_line.endswith("full"): start = log_line.rfind("disk") print(log_line[start:])

When parsing configuration files or log formats where the last occurrence of a delimiter matters, rfind is often the right tool. It keeps the code explicit about the intent: you want the rightmost occurrence, not the first one.

Edge Cases and Compatibility Notes

An empty substring is always considered to be found. rfind("") returns the length of the string (or the end index if specified), because an empty string matches at any position, and the rightmost position is the end. This is consistent with the behavior of find, which returns 0 for an empty substring.

s = "abc" print(s.rfind("")) # 3

When start is greater than end, the search range is empty, and rfind returns -1. The same applies if start is out of bounds; Python clamps the indices to the string length, but the result is still -1 when no match exists.

Compatibility is broad: rfind exists in Python 2 and Python 3, and its behavior has been stable across versions. There is no performance difference between using rfind and using find on a reversed string, but the latter is less readable and more error-prone. Stick with rfind when you need the last occurrence.

For Unicode strings, rfind works on code points, not bytes. This means the index returned is a character index, which is what most Python code expects. If you are working with encoded bytes, you must decode first or use bytes.rfind, which operates on byte sequences. The same logic applies, but the indices refer to byte positions.

python string rfind: Search From the Right | RYUSLOG DEV