String#rindex
str.rindex(substring [, offset]) -> integer or nil The rindex method searches a string from the right side, returning the index of the last occurrence of the specified substring or pattern. Unlike index which finds the first match, rindex finds the last.
Basic Usage
# Find last occurrence
"hello".rindex("l") # => 3
"hello".rindex("x") # => nil
# First vs last
"hello".index("l") # => 2
"hello".rindex("l") # => 3
The basic usage comparison shows the key difference between index and rindex: both return a 0-based position, but one scans from the left and the other from the right. When a character appears multiple times, rindex gives you the position of the rightmost occurrence, which is often the one that marks a meaningful boundary in strings like file paths, URIs, or dotted identifiers.
Practical Examples
File path operations
# Find last slash
path = "/home/user/documents/file.txt"
path.rindex("/") # => 20
# Get filename
filename = path[path.rindex("/") + 1..-1]
# => "file.txt"
The file path example shows rindex being used to find the last directory separator in a Unix path. By searching from the right, you reliably get the position of the final slash, which is the boundary between the directory and the filename. This technique works regardless of how deep the directory tree is, because the last slash is always the one that matters for extracting the filename.
Extension Extraction
filename = "archive.tar.gz"
dot_pos = filename.rindex(".")
extension = filename[dot_pos + 1..-1]
# => "gz"
The extension extraction example uses rindex(".") to find the last dot in a filename like archive.tar.gz. By slicing from that position onward, you get the extension "gz" rather than "tar.gz", which is the behavior most file-handling code expects. This is a small but important detail: index would return the first dot and give you the wrong extension for multi-part filenames.
Finding last match
text = "one two one three one"
text.rindex("one") # => 16 (last occurrence)
text.rindex("three") # => 10
The rindex call with a string argument returns the starting position of the last match, counting from the left. That means the index is always 0-based and relative to the start of the string, even though the search itself scans from the right. This consistency with index makes it easy to swap between the two methods without changing how you interpret the result.
With Offset
# Limit search to before position
"hello world hello".rindex("hello", 10) # => 0 (first "hello")
"hello world hello".rindex("hello") # => 13 (last "hello")
The offset parameter limits the search to positions before the given index, which is useful when you want to find the second-to-last match. By passing the position of the last match as the offset, you can walk backward through a string to enumerate all occurrences in reverse order. This is a less common pattern but can be handy for right-to-left parsing of structured text.
With Regex
"ababab".rindex(/b/) # => 5
"test123abc456".rindex(/\d+/) # => 10 (last digit sequence)
The regex example uses a pattern to find the last sequence of digits in a mixed string. Regex patterns give rindex the same flexibility that index has, so you can search for character classes, alternations, or capture groups from the right side of the string. That makes it easy to extract the final numeric segment from filenames or log entries without writing a manual scanner.
Return Values
# Returns index or nil
result = "hello".rindex("x")
puts result # => nil
# 0-based indexing
"abc".rindex("a") # => 0
"abc".rindex("c") # => 2
The return value is either an integer index or nil, which means you should always check for nil before using the result in arithmetic or slicing. The 0-based indexing is consistent with the rest of Ruby’s string API, so the returned position can be passed directly to [] or range expressions without adjustment.
Use Cases
- Finding last occurrence
- Extracting file extensions
- Parsing paths
- Reverse string search
- Finding last delimiter
Comparison with index
text = "the quick brown fox"
text.index(" ") # => 3 (first space)
text.rindex(" ") # => 16 (last space)
searching from the right
rindex is the better fit when the last occurrence is the one that matters, such as finding a final path separator or the final dot in a filename. It keeps the intent clear because the search direction is part of the method name. When the code later slices the string, the index it returns can be used directly, which keeps the whole operation short and easy to reason about.
That pattern shows up often in small text tasks because the rightmost match is usually the one that marks a boundary. A filename extension, a trailing tag, or the last space in a phrase are all easier to find with a reverse search than by scanning from the front and trying to remember the last hit. The method keeps that logic compact and lets the rest of the code focus on what to do with the position once it is found.
Because the method returns a position instead of the substring itself, it also gives the caller room to decide how to use the result. The code can slice, compare, or ignore the match depending on the situation. That flexibility is part of the appeal: rindex finds the boundary, and the surrounding code decides what the boundary means.
For string-processing code, that separation can be valuable because the search and the action stay distinct. A line parser can find the last delimiter first and then decide whether to trim, split, or discard the rest. That keeps the method easy to reuse in different contexts without forcing a single outcome on the caller.