I'm writing an application in which i receive from a socket a string like
'2227275982'. How would I convert this into dotted quad IP address form?
Every time I try to use the number I get OverflowError: integer literal
too large. Anyone got any ideas?
Thanks,
Scott
Append an L before converting to a number.
Hm... tricky. Time for one of c.l.p's patented analysis threads: Is
it even possible to go from a string of numbers to the dotted quad
form? The example string doesn't produce a legal result, for example,
probably because an extra digit crept in. Starting at the left and
taking as many digits as we can, you get: 222 72 75 982.
From the right, you wind up with: 2227 27 59 82.
If you allow arbitrary numbers of leading zeros, I think it's not
possible to do this unambiguously; is 255000200 255.0.2.0 or
255.0.0.200? If we assume that only a single leading zero is allowed
to indicate an octal number, it's still ambiguous: '102710140' can be
either 102.71.014.0 or 102.71.0.140. If we disallow octal and leading
zeros completely, the same string can be '10.27.10.140' or
'10.27.101.40' or '102.7.10.140', etc. Unless I've missed something,
this problem is unsolvable, so start putting the dots back into your
protocol...
Appended is some evil regex hackery I wrote while attempting to solve
this.
--
A.M. Kuchling http://starship.python.net/crew/amk/
I lack the ability to embellish. Thus you would perforce needs content all
yourselves, one and all, with a rather sparse and uninteresting narrative.
-- Cluracan warms up, in SANDMAN #52: "Cluracan's Tale"
import re
number_pat = re.compile(r"""
# Octal versions
0(?: [123][0-7][0-7] | # Octal 100-377
[1-7][0-7] | # Two digit octal numbers
[1-7] # One digit octal numbers
) |
1\d\d | # Decimal 1xx
2[0-4]\d | # Match decimal numbers from 200 to 249
25[0-5] | # Match decimal numbers from 250 to 255
[1-9]\d | # 2-digit decimal numbers
[0-9] # One-digit decimal numbers
""", re.VERBOSE)
# This pattern matches 4 repetitions of the number pattern, and uses
# ^,$ to require matching the entire string.
quad_pat = re.compile("^" + '('+number_pat.pattern+'){4}$', re.VERBOSE)
# Now we build a list of 4 patterns, all slight variations on the same plan:
# ^( <number pattern> )(?: <number pattern> )(?: <number pattern> )(?: <number pattern> )$
# ^(?: <number pattern> )( <number pattern> )(?: <number pattern> )(?: <number pattern> )$
# ^(?: <number pattern> )(?: <number pattern> )( <number pattern> )(?: <number pattern> )$
# ^(?: <number pattern> )(?: <number pattern> )(?: <number pattern> )( <number pattern> )$
# Using all four of them, you can read off the 4 consecutive quads in group 1 of each pattern.
patlist = []
for i in range(4):
patlist.append( re.compile("^"
+ i*('(?:'+number_pat.pattern+')')
+ '('+number_pat.pattern+')'
+ (3-i)*('(?:'+number_pat.pattern+')') +
"$", re.VERBOSE) )
for ip in ['222727598',
'255255002',
'0000', '102710140',
'020000']:
m = quad_pat.match(ip)
if m is None: print ip, "can't be valid IP address" ; continue
# Print 4 consective quads
print ip, '->',
for p in patlist:
print p.match( ip ).group(1), '.',
print
The string is a representation of the IP address as a long int in network
byte order. For example 16777343 would be 127.0.0.1. So it can't be
translated direcly. What I ended up doing was something like this:
hex (string.atol ('16777343')) which would give me something like
0x100007FL. Then I started from the end and each byte (ie 7F) is a number
in the IP address. I started from the end because hex() did not add the
extra 0 before the one so you'd really not know if the first byte was
supposed to be 01 or 10. Then I just added the results together with the
dots. I think this should work on all systems since the string representation
is always in network byte order. Here is the function I came up with:
import string
def transip (ipstr):
hv = hex (string.atol (ipstr))[2:-1]
p1 = string.atoi (hv[-2:] , 16)
p2 = string.atoi (hv[-4:-2], 16)
p3 = string.atoi (hv[-6:-4], 16)
p4 = string.atoi (hv[:-6] , 16)
return `p1`+'.'+`p2`+'.'+`p3`+'.'+`p4`
A little hackish, perhaps, but it works for me. Thanks to all that replied,
all of your information was very useful for me in reaching this solution.
Cheers,
Scott
On 20 Nov 1999 11:20:51 -0500, Andrew M. Kuchling <akuc...@mems-exchange.org>
wrote:
Oops, there I go solving the wrong problem again.
Have you looked at the struct module? Here's an alternative solution:
def transip2 (ipstr):
# Pack into a 4-byte integer
s = struct.pack('!L', string.atol(ipstr) )
# Unpack as 4 bytes
L = struct.unpack('!4B', s)
return `L[3]`+'.' + `L[2]`+'.'+`L[1]`+'.'+`L[0]`
Also, transip() will crash if the number is so small that it produces
less than 8 digits (unlikely, for real network addresses). There
should be 'hv=string.zfill(hv, 8)' added to it.
--
A.M. Kuchling http://starship.python.net/crew/amk/
When I dream, sometimes I remember how to fly. You just lift one leg, then you
lift the other leg, and you're not standing on anything, and you can fly.
-- Chloe Russell, in SANDMAN #43: "Brief Lives:3"
>>> a = 2227275982L
>>> print "%d.%d.%d.%d" % ((a>>24)&0xFF, (a>>16)&0xFF, (a>>8)&0xFF, a&0xFF)
132.193.136.206
>>>
You may need to play games if the network order doesn't match your
processors byte order but thats just as easy. So is figuring out
which to use automatically.
>def transip2 (ipstr):
> # Pack into a 4-byte integer
> s = struct.pack('!L', string.atol(ipstr) )
>
> # Unpack as 4 bytes
> L = struct.unpack('!4B', s)
>
> return `L[3]`+'.' + `L[2]`+'.'+`L[1]`+'.'+`L[0]`
PMFJI a bit late, but the last line looks a bit tedious to type :-)
packing it the other direction (little endian) enables us using string.join:
>>> from struct import pack, unpack
>>> from string import atol, join
>>> def transip3(long): return join(map(str,unpack('4B', pack('<L',
atol(long)))), '.')
>>> transip3('16777343')
'127.0.0.1'
>>> transip3('2227275982')
'206.136.193.132'
>>>
map-filter-and-reduce-remind-me-of-APL-ly yours
Klaus