# Checking if String.scan does not match

**URL:** <https://www.ruby-forum.com/t/checking-if-string-scan-does-not-match/172965>\
**Category:** Ruby\
**Created:** [July 30, 2009, 4:31pm UTC](https://www.ruby-forum.com/t/checking-if-string-scan-does-not-match/172965 "2009-07-30T16:31:49Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![alexamat](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/bc8723/32.png) [@alexamat](https://www.ruby-forum.com/u/alexamat)\
**Post date:** [July 30, 2009, 4:31pm UTC](https://www.ruby-forum.com/t/checking-if-string-scan-does-not-match/172965/1 "2009-07-30T16:31:49Z")

</div>

Hi everyone,

I’m extracting 3 words from a string delimited with colons using the  
following approach:

str = “test:string:here”  
str.scan(/(.+):(.+):(.+)/) do |a, b, c|  
puts a, b, c  
end

This works fine if all 3 words are present but if you pass a string with  
only 2 words, e.g. “test:string”, the block is not executed.

I need to report an error if the match fails. This could be done like  
this:

str = “test:string”  
matched = false  
str.scan(/(.+):(.+):(.+)/) do |a, b, c|  
puts a, b, c  
matched = true  
end  
if not matched  
puts “invalid format”  
end

but this is messy. Is there a neater, more ruby-like, approach to this?

Any help is greatly appreciated,  
Alex

---

<div class="post-metadata">

**Author:** ![alexamat](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/bc8723/32.png) [@alexamat](https://www.ruby-forum.com/u/alexamat)\
**Post date:** [July 30, 2009, 4:44pm UTC](https://www.ruby-forum.com/t/checking-if-string-scan-does-not-match/172965/2 "2009-07-30T16:44:19Z")

</div>

Alex A. wrote:

> I’m extracting 3 words from a string delimited with colons using the  
> following approach:
> 
> str = “test:string:here”  
> str.scan(/(.+):(.+):(.+)/) do |a, b, c|  
> puts a, b, c  
> end
> 
> This works fine if all 3 words are present but if you pass a string with  
> only 2 words, e.g. “test:string”, the block is not executed.
> 
> I need to report an error if the match fails.

scan is the wrong tool here, because it is intended to match the pattern  
multiple times in the source string. I think all you want is a basic  
regexp match:

str = “test:string:here”  
if /\A(.+):(.+):(.+)\z/ =~ str  
puts $1, $2, $3  
else  
puts “invalid format”  
end

Alternatively written as

case str  
when /\A(.+):(.+):(.+)\z/  
puts $1, $2, $3  
else  
puts “invalid format”  
end

which is useful when there are several different patterns to match  
against the same string.

Beware lines which contain more than 2 colons, because . matches colons  
as well as non-colons. For proper validation you probably want

/\A([^:]+):([^:]+):([^:]+)\z/

Note that . doesn’t match newline. If str may legitimately end with \n,  
but you don’t want to capture the \n in $3, then use \Z instead of \z.

If you are 100% positive that str doesn’t contain more than one line  
then you can use ^ and $ instead of \A and \Z
