Question

How to Match All Regular Expression Occurrences in Ruby

rubyregex

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:

  • match finds the first match
  • =~ returns the index of the first match
  • scan finds 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.
  • scan keeps 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.
Take Quiz

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.

If you want just the names as plain strings, you can flatten the result:

names = text.scan(/Name: (\w+)/).flatten
puts names.inspect
# => ["Alice", "Bob"]

Example: multiple capture groups

text = "x=10 y=20"
pairs = text.scan(/(\w)=(\d+)/)

puts pairs.inspect
# => [["x", "10"], ["y", "20"]]

Each match becomes an array of captured parts.

Block form of scan

You can also process each match as Ruby finds it:

text = "apple banana cherry"
text.scan(/\w+/) do |word|
  puts word.upcase
end

Output:

APPLE
BANANA
CHERRY

This is useful when you want to process matches one at a time instead of storing them all first.

Step by Step Execution

Consider this example:

text = "ID: 12, ID: 34"
result = text.scan(/ID: (\d+)/)
puts result.inspect

Step by step:

  1. text stores the string:

    "ID: 12, ID: 34"
    
  2. Ruby starts scanning from the beginning of the string.

  3. It checks whether /ID: (\d+)/ matches at each position.

  4. It finds the first match:

    "ID: 12"
    

    The capture group (\d+) captures:

    "12"
    
  5. Ruby continues searching after that match.

  6. It finds the second match:

    "ID: 34"
    

    The capture group captures:

    "34"
    
  7. Because scan used one capture group, the final result is:

    [["12"], ["34"]]
    
  8. puts result.inspect prints:

    [["12"], ["34"]]
    

If we remove the capture group:

text = "ID: 12, ID: 34"
result = text.scan(/ID: \d+/)
puts result.inspect
# => ["ID: 12", "ID: 34"]

Now Ruby returns the full matched text instead of captured subparts.

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"]
Take Quiz

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:

text = "cat bat rat"
enum = /\w+at/.to_enum(:scan, text)

matches = []
enum.each do
  m = Regexp.last_match
  matches << { value: m[0], start: m.begin(0), finish: m.end(0) }
end

puts matches.inspect

This is helpful when you need:

  • match positions
  • original full match
  • capture groups with indexes

5. Guard clauses when nothing matches

def extract_ids(text)
  ids = text.scan(/\d+/)
  return [] if ids.empty?

  ids.map(&:to_i)
end

This keeps methods simple and safe.

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

text = "1 2 3"
text.match(/\d+/)
# => only the first match

Use scan instead:

text.scan(/\d+/)
# => ["1", "2", "3"]

Mistake 4: Expecting integers instead of strings

text = "10 20 30"
text.scan(/\d+/)
# => ["10", "20", "30"]

Regex matches strings. Convert them if needed:

text.scan(/\d+/).map(&:to_i)
# => [10, 20, 30]

Mistake 5: Ignoring empty results

text = "no numbers here"
matches = text.scan(/\d+/)
# => []

Always handle the possibility that nothing matches.

Comparisons

TaskRuby methodResult
Find first matchmatchMatchData or nil
Find first match position=~index or nil
Find all matchesscanarray of matches
Iterate through all matches with access to Regexp.last_matchto_enum(:scan, text)enumerator

match vs scan

text = "cat bat rat"

text.match(/\w+at/)[0]
# => "cat"

text.scan(/\w+at/)
# => ["cat", "bat", "rat"]
  • Use match for the first occurrence.
  • Use scan for every occurrence.

scan without groups vs with groups

RegexOutput
/\d+/['12', '34']
/(\d+)/[['12'], ['34']]
/(\w)=(\d+)/[['x', '10'], ['y', '20']]

scan vs manual looping

You could manually loop through matches, but scan is usually shorter and clearer for beginners.

text.scan(/\w+/)

is usually simpler than building custom index-tracking code.

Take Quiz

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 do
  m = Regexp.last_match
  puts [m[0], m.begin(0), m.end(0)].inspect
end

Common rule

If you want all regex matches in Ruby, start with String#scan.

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?

Yes. Ruby will yield each match as it is found.

What happens if nothing matches?

scan returns an empty array:

[].inspect

For example:

"abc".scan(/\d+/)
# => []
Take Quiz

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 scan to find all email addresses.
  • Print the list of matches.
  • Print No emails found if the result is empty.
Take Quiz

Keep learning

Related questions

View more