Question
How can I quickly find every occurrence that matches a regular expression in Ruby?
I looked through Ruby's regular expression features and could not find an obvious method on Regexp for returning all matches from a string. Is there a built-in Ruby way to collect every regex match, ideally with examples of how it works?
Short Answer
By the end of this page, you will understand how Ruby finds all matches for a regular expression, when to use String#scan, how capture groups affect the result, and how to get richer match details with Regexp.last_match or to_enum.
Concept
In Ruby, the most common way to find all occurrences of a regular expression is not by calling a method on Regexp itself, but by calling a method on the string being searched.
The key method is String#scan.
text.scan(/pattern/)
This matters because a regular expression is just the pattern, while the string is the data being searched. Ruby's API reflects that idea: the string performs the search using the regex.
Why this matters
Finding every match is a common task in real programs:
- extracting hashtags from text
- parsing numbers from logs
- finding email addresses in a file
- collecting tokens from user input
- validating and extracting repeated patterns from API responses
The main idea
Ruby gives you several ways to match regex patterns:
matchfinds the first match=~returns the index of the first matchscanfinds all matches
So if you want every occurrence, scan is usually the right tool.
Important behavior with capture groups
scan behaves differently depending on whether your regex contains capture groups.
- No capture groups: returns an array of full matched strings
- With capture groups: returns captured parts instead of the whole match
Example:
"abc 123 def 456".scan(/\d+/)
# => ["123", "456"]
"abc 123 def 456".scan(/(\d+)/)
# => [["123"], ["456"]]
That difference surprises many beginners, so it is important to recognize it early.
Mental Model
Think of a regular expression as a cookie cutter and the string as the dough.
- The regex defines the shape you want.
- The string is what you press the cutter into.
scankeeps pressing the cutter across the whole dough and collects every shape it finds.
If your regex has capture groups, it is like asking Ruby not only to cut out the cookie, but also to separately label parts of it.
- No groups: give me the whole cookie.
- With groups: give me the labeled pieces from each cookie.
Syntax and Examples
Basic syntax
string.scan(regex)
Example:
text = "cat bat rat"
matches = text.scan(/\w+at/)
puts matches.inspect
# => ["cat", "bat", "rat"]
This searches the entire string and returns every substring matching /\w+at/.
Example: extracting numbers
text = "Order 101, item 202, batch 303"
numbers = text.scan(/\d+/)
puts numbers.inspect
# => ["101", "202", "303"]
Here /\d+/ means:
\d= a digit+= one or more times
Example: using capture groups
text = "Name: Alice, Name: Bob"
names = text.scan(/Name: (\w+)/)
puts names.inspect
# => [["Alice"], ["Bob"]]
Because the regex contains a capture group (\w+), scan returns the captured values.
Step by Step Execution
Consider this example:
text = "ID: 12, ID: 34"
result = text.scan(/ID: (\d+)/)
puts result.inspect
Step by step:
-
textstores the string:"ID: 12, ID: 34" -
Ruby starts scanning from the beginning of the string.
-
It checks whether
/ID: (\d+)/matches at each position. -
It finds the first match:
"ID: 12"The capture group
(\d+)captures:"12" -
Ruby continues searching after that match.
-
It finds the second match:
"ID: 34"The capture group captures:
"34"
Real World Use Cases
Extracting data from logs
log = "ERROR 500 at 10:00, ERROR 404 at 10:05"
codes = log.scan(/ERROR (\d+)/).flatten
# => ["500", "404"]
Finding tags or mentions in user content
post = "Hello @alice and @bob"
mentions = post.scan(/@\w+/)
# => ["@alice", "@bob"]
Parsing simple key-value text
config = "host=localhost port=3000"
pairs = config.scan(/(\w+)=([^\s]+)/)
# => [["host", "localhost"], ["port", "3000"]]
Collecting URLs or emails from text
text = "Contact a@example.com or b@example.com"
emails = text.scan(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/)
Tokenizing text for simple processing
sentence = "Ruby is fun!"
words = sentence.scan(/\w+/)
# => ["Ruby", "is", "fun"]
Real Codebase Usage
In real Ruby projects, developers often use scan in a few common ways.
1. Extract then transform
text = "Prices: 12 25 30"
prices = text.scan(/\d+/).map(&:to_i)
# => [12, 25, 30]
This pattern is common when data must be converted after matching.
2. Validation plus extraction
input = "user:alice id:42"
parts = input.scan(/(\w+):(\w+)/)
if parts.empty?
puts "Invalid format"
else
puts parts.inspect
end
Developers often check whether any matches were found before continuing.
3. Using a block to avoid extra arrays
text = File.read("app.log")
text.scan(/ERROR/) do
puts "Found an error"
end
This can be useful when you only need side effects, such as counting or logging.
4. Getting richer match data with an enumerator
Sometimes developers need positions or full MatchData, not just strings. A common pattern is:
Common Mistakes
Mistake 1: Looking for the method on Regexp instead of String
Broken expectation:
regex = /\d+/
# regex.all_matches(text) # no such standard method
Use:
text.scan(/\d+/)
The string performs the search.
Mistake 2: Forgetting how capture groups change the return value
text = "Age: 21, Age: 35"
text.scan(/Age: (\d+)/)
# => [["21"], ["35"]]
A beginner may expect:
["Age: 21", "Age: 35"]
To get full matches, remove the capture group:
text.scan(/Age: \d+/)
Or keep the group and flatten if you only want the captured part:
text.scan(/Age: (\d+)/).flatten
Mistake 3: Using match when you need all matches
Comparisons
| Task | Ruby method | Result |
|---|---|---|
| Find first match | match | MatchData or nil |
| Find first match position | =~ | index or nil |
| Find all matches | scan | array of matches |
Iterate through all matches with access to Regexp.last_match | to_enum(:scan, text) | enumerator |
match vs scan
Cheat Sheet
Quick reference
Find all matches
text.scan(/pattern/)
Return value rules
- No capture groups: array of full matches
- One capture group: array of one-element arrays or values yielded to block
- Multiple capture groups: array of arrays
Examples
"a1 b2 c3".scan(/\d/)
# => ["1", "2", "3"]
"a1 b2 c3".scan(/(\d)/)
# => [["1"], ["2"], ["3"]]
"x=10 y=20".scan(/(\w)=(\d+)/)
# => [["x", "10"], ["y", "20"]]
Convert matched numbers
text.scan(/\d+/).map(&:to_i)
Process with a block
text.scan(/\w+/) do |word|
puts word
end
Get match positions
enum = /pattern/.to_enum(:scan, text)
enum.each
m = .last_match
puts [m[], m.(), m.()].inspect
FAQ
How do I get all regex matches in Ruby?
Use String#scan:
text.scan(/pattern/)
Why does scan return nested arrays sometimes?
That happens when your regex contains capture groups. Ruby returns the captured parts for each match.
How do I get the full match instead of capture groups?
Remove the capture groups, or use a different pattern that matches the whole text you want.
Does scan return strings or MatchData?
scan normally returns strings or arrays of captured strings. If you need MatchData, use an enumerator with to_enum(:scan, text) and inspect Regexp.last_match.
How do I find match positions in Ruby?
Use:
enum = /pattern/.to_enum(:scan, text)
enum.each do
m = Regexp.last_match
puts m.begin(0)
end
Can I use scan with a block?
Mini Project
Description
Build a small Ruby script that extracts all email addresses from a block of text. This demonstrates how String#scan is used in a realistic parsing task where you want every occurrence, not just the first one.
Goal
Create a Ruby program that finds every email address in a string and prints them as a clean list.
Requirements
- Store a block of text in a string variable.
- Use a regular expression with
scanto find all email addresses. - Print the list of matches.
- Print
No emails foundif the result is empty.
Keep learning
Related questions
How to Call Shell Commands from Ruby and Capture Output
Learn how to run shell commands in Ruby, capture output, check exit status, and choose the right method for scripts and apps.
How to Check Whether a String Contains a Substring in Ruby
Learn how to check if a string contains a substring in Ruby using include?, match, and multiline string examples.
How to Check if a Hash Key Exists in Ruby
Learn how to check whether a specific key exists in a Ruby hash using key?, has_key?, and include? with clear examples.