将Python中的“小端”十六进制字符串转换为IP地址

11

如何将这种形式的字符串转换为IP地址?例如:"0200A8C0"。字符串中的“八进制”是反向的,也就是说,给定的示例字符串应生成192.168.0.2

5个回答

35

通过socket模块提供了网络地址操作。

socket.inet_ntoa(packed_ip)

将32位打包的IPv4地址(一个长度为4个字符的字符串)转换为其标准的点分十进制表示(例如,“123.45.67.89”)。当与使用标准C库并需要类型为struct in_addr的对象的程序交互时,这是有用的,这是32位打包二进制数据的C类型作为该函数接受的参数。

您可以使用小端无符号长格式和struct.pack()将十六进制字符串转换为打包的IP。

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'

1
你需要使用"<I"而不是"L"作为pack的第一个参数,尽管"<L"也可以工作。否则,你会暴露给平台字节序的影响。 - Omnifarious
@Omnifarious,它的“long/int”特性在这里会产生什么影响? - Matt Joiner
192.168.10.5 的十六进制为 c0a80a05。当我使用这段代码将十六进制转换为IP地址时,得到的结果是 5.10.168.192。虽然这个结果是正确的,但是顺序被颠倒了。为了让它正常工作,我必须将 <L 改为 >L,但如果我从用户那里获取输入并不知道输入的十六进制值,这样做就没有意义了!你有什么办法可以让它适用于任何十六进制值吗?谢谢。 - Tes3awy

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

这段话简短明了,您可以将其放入一个带有错误检查的函数中以满足您的需求。


便捷的分组函数:

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

@Roger,zip(*[iter(s)]*2) 是怎么工作的呢?我非常感兴趣。 - Matt Joiner
4
真是个巧妙的方法!iter(s)返回字符串上的一个迭代器。将该迭代器列表乘以2会创建两个引用指向同一迭代器zip()返回一个元组列表,其中包含来自其参数中的每个元素。由于两个参数都是相同的迭代器,它将为每个元组提取两次,返回相邻2个字符的元组。我从未想过尝试这样的事情。 :D - Max Shawabkeh
@Matt:这是我看过的大多数“分组”配方的核心,Max解释得很好。将更新以包括两个类似的短函数。 - Roger Pate
@Roger,非常感谢,我一直在期待像你这样的解决方案出现。 - Matt Joiner
我创建了一个新问题,直接询问您在这里解决的问题:https://dev59.com/T0vSa4cB1Zd3GeqPhMYR - Matt Joiner
+1 那个 zip 迭代技巧对于代码高尔夫来说非常有用。太棒了。 - Exelian

3
你可以像这样做:

你可以这样做:

>>> 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

八位分割可能可以更加优雅地完成,但是我暂时想不到更简单的方法。

编辑:或者在一行上:

>>> 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

是的,尤其是分组让我很困扰。我找不到一个好的方法来“切块”八位字节,并且以相反的顺序迭代它们,希望有人知道一种方法。 - Matt Joiner
@Max,Roger的回答包含了一种非常有趣的方法来实现这个。 - Matt Joiner

0

你可以创建一个简单的函数来将十六进制转换为十进制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)

0

我的尝试:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接