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
| Parameter | Type | Default | Description |
|---|---|---|---|
pattern | Regexp or String | — | The 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.