Here's what I consider a slightly cleaner, more robust version:

require 'strscan'
inp = "3:ab23:cat5:sheep"
s = StringScanner.new( inp )
words = []
until s.eos?
  begin
    unless digits = s.scan( /\d+(?=:)/ )
      raise "I can't find an integer followed by a colon"
    end
    s.pos += 1 # skip the colon we know is there
    digits = digits.to_i
    unless s.rest_size >= digits
      raise "I ran out of characters; looking for #{digits} characters,
#{s.rest_size} left"
    end
    words << s.peek( digits )
    s.pos += digits
  rescue RuntimeError => e
    warn "Looking at #{s.rest.inspect},"
    warn e.message
    abort "Words found so far: #{words.inspect}"
  end
end
puts "Words found: ", words