0

I would like to convert either 0x123, 012 or 36#foo into an integer.

To do so, I wrote this:

def str2int(s):
    if re.findall('(?i)^0x',s):
        return int(s, 16)
    if re.findall('^(?i)0',s):
        return int(s, 8)
    m = re.findall('(?i)^(\d+)\#([0-9a-z]+)',s)
    if m:
        return int(m[0][1], m[0][0])
    raise AssertionError, 'Unknown value'

I feel it is a bit complicated. Is there any builtin method?

1
  • 1
    I think if you want to interpret arbitrary bases, your solution is the best you’ll find for this. There is no built-in way for this because base#number is not a built-in supported expression format. Commented Nov 25, 2015 at 13:13

3 Answers 3

2

Yes, you can use ast.literal_eval.

>>> import ast
>>> ast.literal_eval("0x123")
291
>>> ast.literal_eval("012")
10
>>> ast.literal_eval("36#foo")
36

However, note that literal_eval("012") will only work in 2.7 and lower because 3.x no longer supports that style of octal literal. But this will work:

>>> ast.literal_eval("0o12")
10
Sign up to request clarification or add additional context in comments.

5 Comments

It does not work. echo $(( 36#foo )) gives 20328 not 36
Oh. In that case, there's no built-in that does that.
Well, I guess nobody will use it anymore. It is just fun :)
@nowox How does 'echo $(( 36#foo ))' give you 20328? Also how would you expect that to be parsed? It’s not even following any pattern (and it’s so very different than your examples in the question).
That's base 36. int('foo', 36) -> 20328.
2

Solution without regular expressions:

def convert (s):
    if s.lower().startswith('0x'):
        s = '16#' + s[2:]
    elif s.startswith('0'):
        s = '8#' + s[1:]
    elif '#' not in s:
        s = '10#' + s
    base, num = s.split('#', 1)
    return int(num, int(base))
>>> testcases = [('0x123', 291), ('012', 10), ('36#foo', 20328)]
>>> for s, n in testcases:
        print(s, n, n == convert(s))

0x123 291 True
012 10 True
36#foo 20328 True

1 Comment

This is a good answer -- while you are at it maybe you could tweak it to handle binary literals to make it even more flexible.
1

int will do, when you pass 0 as second argument:

   int('0x123', 0)
=> 291
   int('0o12', 0)
=> 10

If you want to support comments, str.partition is the simplest way I can think about:

   int('36#foo'.partition('#')[0], 0)
=> 36

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.