12

What's the best way to turn a string in this form into an IP address: "0200A8C0". The "octets" present in the string are in reverse order, i.e. the given example string should generate 192.168.0.2.

0

6 Answers 6

36

Network address manipulation is provided by the socket module.

socket.inet_ntoa(packed_ip)

Convert a 32-bit packed IPv4 address (a string four characters in length) to its standard dotted-quad string representation (for example, ‘123.45.67.89’). This is useful when conversing with a program that uses the standard C library and needs objects of type struct in_addr, which is the C type for the 32-bit packed binary data this function takes as an argument.

You can translate your hex string to packed ip using struct.pack() and the little endian, unsigned long format.

s = "0200A8C0"

import socket
import struct
addr_long = int(s, 16)
print(hex(addr_long))  # '0x200a8c0'
print(struct.pack("<L", addr_long))  # '\xc0\xa8\x00\x02'
print(socket.inet_ntoa(struct.pack("<L", addr_long)))  # '192.168.0.2'
Sign up to request clarification or add additional context in comments.

3 Comments

You want "<I", not "L" as the first argument to pack, though "<L" will work too. Otherwise you're leaving yourself open to whatever the platform endianess is.
@Omnifarious, how exactly does it's "long/int"ness effect it here?
192.168.10.5 hex is c0a80a05. When I use this snippet to convert from HEX to IP, I get 5.10.168.192. Which is correct but reversed. To make it work I have to change <L to >L which doesn't make any sense if I take the input from a user and I don't know what HEX is the input! Do you have any idea how to make it work for any HEX value? Thank you
8
>>> s = "0200A8C0"
>>> bytes = ["".join(x) for x in zip(*[iter(s)]*2)]
>>> bytes
['02', '00', 'A8', 'C0']
>>> bytes = [int(x, 16) for x in bytes]
>>> bytes
[2, 0, 168, 192]
>>> print ".".join(str(x) for x in reversed(bytes))
192.168.0.2

It is short and clear; wrap it up in a function with error checking to suit your needs.


Handy grouping functions:

def group(iterable, n=2, missing=None, longest=True):
  """Group from a single iterable into groups of n.

  Derived from http://bugs.python.org/issue1643
  """
  if n < 1:
    raise ValueError("invalid n")
  args = (iter(iterable),) * n
  if longest:
    return itertools.izip_longest(*args, fillvalue=missing)
  else:
    return itertools.izip(*args)

def group_some(iterable, n=2):
  """Group from a single iterable into groups of at most n."""
  if n < 1:
    raise ValueError("invalid n")
  iterable = iter(iterable)
  while True:
    L = list(itertools.islice(iterable, n))
    if L:
      yield L
    else:
      break

6 Comments

@Roger, how does that zip(*[iter(s)]*2) work? I'm very interested in that.
This is quite the hack! iter(s) returns an iterator over the string. Multiplying the list of this iterator by 2 will create two references to the same iterator. zip() returns a list of tuples containing an element from each of its arguments. Since both arguments are the same iterator, it will take from it twice for each tuple, returning a tuple of each 2 adjacent characters. I would never have thought of trying anything like this. :D
@Matt: It's the core of most "grouping" recipes I've seen, and Max explained it well. Will update to include two short functions in that vein.
@Roger, thanks very much, I was hoping a solution like yours would come up.
I've created a new question, which directly asks what you've solved here: stackoverflow.com/questions/2202461/…
|
3

You could do something like this:

>>> s = '0200A8C0'
>>> octets = [s[i:i+2] for i in range(0, len(s), 2)]
>>> ip = [int(i, 16) for i in reversed(octets)]
>>> ip_formatted = '.'.join(str(i) for i in ip)
>>> print ip_formatted
192.168.0.2

The octet splitting could probably be done more elegantly, but I can't think of a simpler way off the top of my head.

EDIT: Or on one line:

>>> s = '0200A8C0'
>>> print '.'.join(str(int(i, 16)) for i in reversed([s[i:i+2] for i in range(0, len(s), 2)]))
192.168.0.2

2 Comments

yeah teh grouping in particular gets to me. i can't find a nice way to "chunk" the octets out, and iterate those in reverse order, i'm hoping someone knows a way
@Max, Roger's answer contains a very intriguing way to do this.
0

My try:

a = '0200A8C0'
indices = range(0, 8, 2)
data = [str(int(a[x:x+2], 16)) for x in indices]
'.'.join(reversed(data))

Comments

0

In "modern" Python I do it like this (short, simple, pythonic, concise, clear and no additional imports requires)

hex_address = '0200A8C0'  # 4 bytes in little-endian notation
ip_address = '.'.join(map(str, reversed(bytearray.fromhex(hex_address))))

print(ip_address)  # prints '192.168.0.2'

Steps

  • the builtin bytearray.fromhex method converts the 4 bytes hexidecimal address string to a iterable of 4 integers
  • the iteration direction is reversed before joining because hex_address is in little-endian notation (the least-significant byte at the smallest address)
  • integers are converted to a str so they can be joined with the . character

Comments

-1

A simple def you can make to convert from hex to decimal-ip:

def hex2ip(iphex):
    ip = ["".join(x) for x in zip(*[iter(str(iphex))]*2)]
    ip = [int(x, 16) for x in ip]
    ip = ".".join(str(x) for x in (ip))
    return ip

# And to use it:
ip = "ac10fc40"
ip = hex2ip(iphex)
print(ip)

1 Comment

Downvoted since it does not add anything to the already provided answers, no extra explanation given, etc.

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.