How to validate that a string is a proper hexadecimal value in Ruby?

Viewed 1453

I am writing a 6502 assembler in Ruby. I am looking for a way to validate hexadecimal operands in string form. I understand that the String object provides a "hex" method to return a number, but here's a problem I run into:

  "0A".hex #=> 10 - a valid hexadecimal value
  "0Z".hex #=>  0 - invalid, produces a zero
"asfd".hex #=> 10 - Why 10?  I guess it reads 'a' first and stops at 's'?

You will get some odd results by typing in a bunch of gibberish. What I need is a way to first verify that the value is a legit hex string.

I was playing around with regular expressions, and realized I can do this:

true if "0A" =~ /[A-Fa-f0-9]/
#=> true

true if "0Z" =~ /[A-Fa-f0-9]/
#=> true <-- PROBLEM

I'm not sure how to address this issue. I need to be able to verify that letters are only A-F and that if it is just numbers that is ok too.

I'm hoping to avoid spaghetti code, riddled with "if" statements. I am hoping that someone could provide a "one-liner" or some form of elegent code.

Thanks!

5 Answers

!str[/\H/] will look for invalid hex values.

String#hex does not interpret the whole string as hex, it extracts from the beginning of the string up to as far as it can be interpreted as hex. With "0Z", the "0" is valid hex, so it interpreted that part. With "asfd", the "a" is valid hex, so it interpreted that part.

One method:

str.to_i(16).to_s(16) == str.downcase

Another:

str =~ /\A[a-f0-9]+\Z/i   # or simply /\A\h+\Z/ (see hirolau's answer)

About your regex, you have to use anchors (\A for begin of string and \Z for end of string) to say that you want the full string to match. Also, the + repeats the match for one or more characters.

Note that you could use ^ (begin of line) and $ (end of line), but this would allow strings like "something\n0A" to pass.

This is an old question, but I just had the issue myself. I opted for this in my code:

str =~ /^\h+$/

It has the added benefit of returning nil if str is nil.

Since Ruby has literal hex built-in, you can eval the string and rescue the SyntaxError

eval "0xA" => 10

eval "0xZ" => SyntaxError

You can use this on a method like

def is_hex?(str)
  begin
    eval("0x#{str}")
    true
  rescue SyntaxError
    false
  end
end

is_hex?('0A') => true
is_hex?('0Z') => false

Of course since you are using eval, make sure you are sending only safe values to the methods

Related