Convert "little endian" hex string to IP address in Python
Asked Answered
R

6

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.

Ruben answered 4/2, 2010 at 7:16 Comment(0)
F
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'
Forkey answered 4/2, 2010 at 7:39 Comment(3)
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.Kohima
@Omnifarious, how exactly does it's "long/int"ness effect it here?Ruben
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 youHanna
H
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
Hypoploid answered 4/2, 2010 at 7:32 Comment(6)
@Roger, how does that zip(*[iter(s)]*2) work? I'm very interested in that.Ruben
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. :DPantomime
@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.Hypoploid
@Roger, thanks very much, I was hoping a solution like yours would come up.Ruben
I've created a new question, which directly asks what you've solved here: #2202961Ruben
+1 That zip iter trick is mindbogglingly useful for code golf. Awesome.Valencia
P
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
Pantomime answered 4/2, 2010 at 7:25 Comment(2)
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 wayRuben
@Max, Roger's answer contains a very intriguing way to do this.Ruben
H
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))
Hydrometer answered 4/2, 2010 at 7:40 Comment(0)
L
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
Lapotin answered 2/4, 2024 at 8:4 Comment(0)
M
-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)
Munshi answered 12/12, 2022 at 12:36 Comment(1)
Downvoted since it does not add anything to the already provided answers, no extra explanation given, etc.Lapotin

© 2022 - 2025 — McMap. All rights reserved.