rubyguides

String#scan

str.scan(pattern) -> array

The scan method searches a string for all occurrences of a given pattern and returns an array containing each match. Unlike match which returns only the first match, scan finds every occurrence.

Syntax

string.scan(pattern)

When given a regex pattern, scan returns an array of all matching substrings. If the regex contains capturing groups, each element of the returned array is an array of the captured groups.

Parameters

ParameterTypeDefaultDescription
patternRegexp or StringThe pattern to search for. Can be a regular expression or a plain string.

Examples

Basic string matching

text = "the quick brown fox jumps over the lazy dog"

text.scan("the")
# => ["the", "the"]

This is the most direct form of scan: give it a plain string and collect every exact match. It is a nice fit when the text you want to find is fixed and you do not need any regex features. The result stays easy to inspect because every match appears in order.

Using regular expressions

text = "I have 3 apples and 5 oranges"

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

text.scan(/[aeiou]/)
# => ["a", "e", "a", "o", "e", "a", "o", "u", "e"]

Regular expressions are where scan starts to feel especially flexible. Once the pattern can express character classes or repeated structure, the same method can search for many different kinds of text without changing the surrounding code. That makes it useful for lightweight parsing and extraction.

With capturing groups

When the pattern contains capturing groups, scan returns arrays of the captured groups:

text = "John:25, Jane:30, Bob:22"

text.scan(/(\w+):(\d+)/)
# => [["John", "25"], ["Jane", "30"], ["Bob", "22"]]

Captured groups are helpful when each match contains more than one piece of information. Instead of splitting the string again later, scan can return the pieces already separated for you. That makes the output easier to map into hashes, records, or other structured data.

Extracting emails from text

content = "Contact us at support@example.com or sales@company.org for help"

content.scan(/[\w.-]+@[\w.-]+\.\w+/)
# => ["support@example.com", "sales@company.org"]

Email extraction is a classic example of why scan is useful in real text processing. The pattern stays compact, but it still pulls out every matching item in one pass. That can be enough for reports, validation tools, or quick content checks.

Finding all words of a certain length

sentence = "Ruby is a powerful and expressive programming language"

sentence.scan(/\b\w{4,}\b/)
# => ["Ruby", "powerful", "expressive", "programming", "language"]

This example shows how scan can act as a simple text filter. By adjusting the regular expression, you can target just the words that fit a size rule and leave the rest behind. That pattern is handy in search tools, analyzers, and content cleanup scripts.

Common Patterns

With block form

When given a block, scan yields each match and returns the original string:

"hello world".scan(/\w+/) { |word| puts word.upcase }
# HELLO
# WORLD
# => "hello world"

The block form is useful when you want to react to each match immediately. It keeps the original string intact while still giving you a chance to print, transform, or collect each match one by one. That can be a better fit than building a temporary array first.

Building a word frequency counter

text = "the quick brown fox jumps over the lazy dog the fox is quick"

words = text.scan(/\b\w+\b/)
frequency = words.tally
# => {"the"=>2, "quick"=>2, "brown"=>1, "fox"=>2, "jumps"=>1, ...}

Frequency counting is a natural follow-up when the matches themselves matter more than the original sentence. Once scan has turned the string into words, array methods can finish the job with very little extra code. That keeps the whole pipeline compact and easy to review.

Parsing structured data

data = "ID:001,ID:002,ID:003"

ids = data.scan(/ID:(\d+)/).flatten
# => ["001", "002", "003"]

Structured data often hides inside text that is only partly regular, and scan gives you a practical way to pull those pieces back out. Flattening the captures is a small extra step, but it turns the result into a plain list that is easy to pass elsewhere.

Practical Tips

scan is useful when the full set of matches matters more than the first one. It fits naturally in log parsing, token extraction, and text cleanup tasks where repeated patterns need to be collected from one string.

The block form is helpful when you want to process matches as they appear instead of building a final array first. That can make formatting, counting, or streaming work a little simpler.

See Also