2

I have a string, populated from the *nix "hostname" command, that I need to parse for a number. That's the easy part. The difficulty comes from a need to have to Do Math(tm) on that captured number. Apparently regex captures are always of type MatchData, which doesn't have any math functions like 'add' or 'modulo', nor does it have a method for ".to_i". Currently, in order to Do Math(tm) on my captured number I need to use MatchData's .to_s method to convert the capture to a string, then use String's .to_i to make it an integer. My question is, what's the better way to do this?

hostname = "webserver1337.mycorp.com"
number = hostname.match(/[a-z]+/) 

puts "#{number}, with class #{number.class}" # prints '1337, with class MatchData'

somevar = number + 1 # this will fail horribly

temp1 = number.to_s
number = temp1.to_i

someothervar = number + 1

puts "#{number}, #{someothervar} with class #{number.class}" # prints '1337, 1338 with class FixNum'

This is... slightly ugly. Is there a better/cleaner way to achieve the same thing?

3
  • Your code don't made what you say. number == webserver not 1337 Commented Mar 10, 2010 at 17:08
  • You really don't need temp1: number = number.to_s.to_i will suffice. Commented Mar 10, 2010 at 17:13
  • Mark, that's exactly what I was looking for (well, ideally a .to_i method for MatchData would exist, but patching the class is... heavy-handed for this script), thank you so much! Commented Mar 10, 2010 at 17:22

4 Answers 4

7

The MatchData object will return the string(s) contained in the match by using []. For example:

hostname = "webserver1337.mycorp.com"
m = hostname.match( /([a-z]+)(\d+)/ ) 
number = m[2].to_i + 1
p m[0], m[1], m[2], number

To do it one shot:

number = hostname.match(/\d+/)[0].to_i + 1
Sign up to request clarification or add additional context in comments.

1 Comment

That didn't work for me but what did was more like: number = (hostname.match /\d+/).[0].to_i +1
3

hostname = "webserver1337.mycorp.com"

number = hostname[/\d+/].to_i + 1

2 Comments

number = hostname[/\d+/].to_i + 1 rescue nil
Actually, String::[regex] doesn't throw; it returns nil on a failure to match. And nil.to_i is 0. the only thing the rescue would catch is when hostname is nil (or something other than string).
1

All data extract from a regexp are define like a string because it's extract from a String. Even if you extract digit data explicit


"webserver1337.mycorp.com" =~ /(\d+)/
p $1 #=> "1337"
puts $1.class #=> String

Comments

0

The top match would not handle a hostname with a number in the middle. i.e. foo23bar03 or 32bar30 would get the wrong number.

something like anchors the number match to the word boundary and gets the number out reliably:

number = hostname.match(/(\d+\b)/)[0]

building on this I made:

#!/bin/env ruby
# simple test of hostname parser
class Hostname
  attr_reader :name, :number, :full_name

  def initialize(hostname)
    parse = hostname.match(/([A-Za-z0-9\-\_]+[A-Za-z\-\_]+)(\d+\b)/).to_a || []
    @full_name = parse.fetch(0, hostname)
    @name      = parse.fetch(1, hostname)
    @number    = parse.fetch(2, hostname).to_i
  end
end

I wrote specs for it and output up at this gist: https://gist.github.com/spheromak/9147685

I believe it could be shorter with a negative match instead of a positive match, but this seemed to get it done for me!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.