String#match
str.match(pattern) -> MatchData or nil The match method searches a string for a pattern match. It returns a MatchData object on success or nil if no match is found. For simple boolean checks, use match? which is faster and returns true or false.
Syntax
str.match(pattern)
str.match(pattern, index)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
pattern | Regexp or String | — | The pattern to search for. Strings are converted to Regexp. |
index | Integer | 0 | Start position in the string to begin searching. |
Examples
Basic regex matching
text = "Hello, World!"
result = text.match(/World/)
result[0] # => "World"
result.begin(0) # => 7
result.end(0) # => 12
The MatchData object returned by match gives you the matched substring and its position in the original string. You can use begin and end to find exactly where the match starts and finishes, which is useful for highlighting search results or extracting the surrounding context. When no match is found, match returns nil rather than raising an error, so always guard with an if before calling methods on the result.
Capturing groups
date = "2026-03-09"
match = date.match(/(\d{4})-(\d{2})-(\d{2})/)
match[0] # => "2026-03-09" (full match)
match[1] # => "2026" (first group)
match[2] # => "03" (second group)
match[3] # => "09" (third group)
match.captures # => ["2026", "03", "09"]
Capture groups let you pull structured data out of a string in one pass. Index 0 refers to the entire match, while indices 1 and above correspond to each parenthesised group from left to right. Calling captures returns only the group values without the full match, which is handy when you only care about the extracted pieces.
Using match? for boolean checks
email = "user@example.com"
email.match?(/\w+@\w+\.\w+/) # => true
email.match?(/^\d+$/) # => false
# match? is faster when you only need true/false
if email.match?(/@/)
puts "Has @ symbol"
end
The match? method is faster than match because it does not build a MatchData object — it returns a simple boolean. Use it for guard clauses, validation checks, and any condition where you only need to know whether the pattern appears. For extraction tasks, fall back to match so you can access the captured groups.
Finding named captures
text = "Name: Ada"
match = text.match(/Name: (?<name>\w+)/)
match[:name] # => "Ada"
match["name"] # => "Ada"
match.captures # => ["Ada"]
Named captures make complex patterns more readable by giving each group a descriptive label. Instead of remembering that match[3] is the zip code, you can write match[:zip] and let the pattern document itself. Both the symbol form (match[:name]) and the string form (match["name"]) work, so you can choose whichever fits your style.
Start position parameter
Named captures keep complex patterns readable, but sometimes you need to search the same string more than once. The index parameter lets you resume a search where the previous match left off, which is useful for finding every occurrence of a pattern with full positional information.
text = "hello hello"
match1 = text.match(/hello/)
match2 = text.match(/hello/, match1.end(0))
match1[0] # => "hello"
match2[0] # => "hello" (second occurrence)
The index parameter makes match suitable for sequential searches through a long string, such as finding all matches in a log file or a document. By passing the end position of each match as the start for the next call, you walk through the string without re-scanning from the beginning.
Common Patterns
Validation with captures
def extract_phone(text)
match = text.match(/(\d{3})-(\d{3})-(\d{4})/)
return nil unless match
{ area: match[1], prefix: match[2], line: match[3] }
end
extract_phone("Call me at 555-123-4567")
# => { area: "555", prefix: "123", line: "4567" }
Returning nil when no match is found is a convention that keeps the calling code simple: you can use the result in a conditional or return early with a default value. This pattern is common in parsers that need to try several patterns in sequence, stopping at the first one that succeeds.
Iterative matching with block
text = "one1 two2 three3"
results = []
text.scan(/\d+/) { |m| results << m }
results # => ["1", "2", "3"]
Using scan with a block is more concise than calling match repeatedly when you want every match in the string. The block receives each match as a string, and you can collect or transform the values as needed. Note that scan does not return MatchData objects, so you cannot access capture positions — use match in a loop if you need those details.
Safe parsing
def parse_version(header)
match = header.match(/Ruby (\d+\.\d+)/)
match ? match[1] : nil
end
parse_version("Using Ruby 3.4") # => "3.4"
parse_version("No version here") # => nil
Read match as structured search
match is a good fit when the result should carry more than a yes-or-no answer. It gives you the matched text, the capture groups, and the positions that describe where the pattern landed in the string. That extra structure makes it useful for parsing, validation, and any task where the match itself contains data you want to keep. The method is more expressive than a boolean check because it turns the search into a small object you can inspect.
Use captures to name the pieces
Capture groups are the part of match that usually make the method feel worth reaching for. They let you pull apart a date, a phone number, or a version string without slicing the input by hand. Named captures can make that even clearer because the resulting keys say what each piece means. When the pattern has important subparts, match gives you a direct path from the original string to the values you care about.
Keep boolean checks separate
If you only need to know whether something is present, match? is often the better choice because it avoids building a MatchData object. That split keeps the code honest about its intent: use match when you need the details, and match? when you only need the answer. In a codebase, that distinction helps the reader understand whether the next lines are going to inspect captures or just branch on a yes-or-no result.