从Python 3.x中的列表中提取IP和端口

时间:2017-09-15 19:50:09

标签: python python-3.x

我想从返回的列表中提取IP和端口。我目前正在使用str(var).replace命令删除多余的字符。当字符串格式改变使.replace命令通过错误

时,这将导致问题
def discover_device():
    """ This function will look for available device on the local network and extract the IP from the result"""
    discover_device = '[<Device: 192.168.222.123:8075>]' # Actually: call to broadcasting device
    device_ip = str(discover_device).replace('[<Device: ', '').replace(':8075>]', '')

所以如果出现以下问题:     [<Device: xxx.xxx.xxx.xxx:xxxx>]

更改为:     [<now_what: xxx.xxx.xxx.xxx:xxxx>]

dicovery_device()将通过并发生错误。

识别ip / port模式并提取ip和port而不必依赖周围字符的完整性的最佳做法是什么?

来自:[<Device: 192.168.222.123:8075>]

对此:192.168.222.123:8075

并且最好:[192.168.222.123, 8075]

考虑点块内的IP差异和基于16位的最大端口号(通常在冒号后的4个整数,最多5个整数)

4 个答案:

答案 0 :(得分:4)

这不需要正则表达式。使用str的内置方法split

>>> device = '[<Device: 192.168.222.123:8075>]'
>>> _, ip, port = device.strip('[<>]').split(':')
>>> print((ip.strip(), port))
('192.168.222.123', '8075')

如果真的想要使用正则表达式,我会使用一个简单的:

>>> import re
>>> ip, port = re.findall('([\d.]+)', device)
>>> print((ip, port))
('192.168.222.123', '8075')

答案 1 :(得分:3)

假设有一个IPv4地址,请尝试提取数字和关键标点符号。然后在必要时切片有效结果。验证IP地址也可能是一种更安全的方法。

在Python 3中:

<强>代码

import string
import ipaddress


def validate_port(func):
    """Return the results or raise and exception for invalid ports."""
    def wrapper(arg):
        result = func(arg)
        if len(result) == 2 and not result[-1].isdigit():
            raise ValueError("Invalid port number.")
        return result
    return wrapper


@validate_port
def discover_device(device):
    """Return a list of ip and optional port number.  Raise exception for invalid ip."""
    result = "".join(i for i in device if i in (string.digits +".:")).strip(":").split(":")

    try:
        ipaddress.ip_address(result[0])
    except ValueError as e:
        # Numbers in the device name (index 0) or invalid ip
        try:
            ipaddress.ip_address(result[1])
        except IndexError:
            raise e
        else:
            return result[1:]
    else:
        return result

<强>演示

discover_device("[<Device: 192.168.222.123>]")
# ['192.168.222.123']

discover_device("[<Device: 192.168.222.123:8075>]")
# ['192.168.222.123', '8075']

discover_device("[<Device.34: 192.168.222.123:8080>]")
# ['192.168.222.123', '8080']

discover_device("[<Device: 192.168.222123>]")
# ValueError: '192.168.222123' does not appear to be an IPv4 or IPv6 address

discover_device("[<Device21: 192.168.222123>]")
# ValueError: '192.168.222123' does not appear to be an IPv4 or IPv6 address

discover_device("[<device.451: 192.168.222.123:80.805>]")
# ValueError: Invalid port number.

功能

  • 对周围人物不敏感
  • IP地址验证(非IPv6)和异常处理
  • 防范设备名称中的数字
  • 验证端口号(可选)

<强>详情

通常result是包含ip和可选端口号的列表。但是,如果数字在设备名称中,则结果的第一个索引将包含不需要的数字。以下是result

的示例
    # ['192.168.222.123']                                  ip   
    # ['192.168.222.123', '8075']                          ip, port
    # ['192.168.222123']                                   invalid ip
    # ['.34', '192.168.222.123', '8080']                   device #, ip, port
    # ['192.168.222.123', '80.805']                        invalid port

异常处理测试设备名称中的数字并验证第一个或第二个索引中的IP地址。如果没有找到,则会引发异常。

虽然验证端口号超出了问题的范围,但端口被假定为数字。一个简单的测试被添加到validate_port装饰器,可以根据需要应用或更新。装饰器屏蔽discover_device()的输出。如果端口不是纯数字,则会引发异常。有关修改限制的信息,请参阅this post。有关Python装饰器的精彩教程,请参阅this blog

选项

如果验证不是问题,则只要设备名称中没有".",以下代码就足够了:

def discover_device(device):
    result = "".join(i for i in device if i in (string.digits +".:")).strip(":").split(":")
    if "." not in result[0]:
        return result[1:]
    return result

如果首选非装饰器解决方案,请定义以下函数:

def validate_port(result):
    """Return the results or raise and exception for invalid ports."""
        if len(result) == 2 and not result[-1].isdigit():
            raise ValueError("Invalid port number.")
        return result

现在将discover_device()的返回值传递给后一个函数,即return validate_port(result[1:])return validate_port(result)

关于@coder的建议。

答案 2 :(得分:2)

您只需使用enter image description here即可找到IP地址,与以前无关。

例如这一个:

\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b

作为测试:

>>> import re
>>> re.findall(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b', '[<Device: 192.168.222.123:8075>]')
['192.168.222.123']
>>> re.findall(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b', '[<SomethingElse: 192.168.222.123:8075>]')
['192.168.222.123']
>>> re.findall(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{,5}', '[<SomethingElse: 192.168.222.123:8075>]')
['192.168.222.123:8075']

答案 3 :(得分:-1)

我认为你最好的选择是使用正则表达式:

import re

def discover_device(in_str):
    m = re.search('(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:\:\d{1,5})?)', in_str)
    if m:
        return m.group(0)
    else:
        return None

如果您的正则表达式字符串为(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:\:\d{1,5})?),则细分为:

  • \d{1,3}\.查找1到3位数后跟一段时间
  • (?:\:\d{1,5})?查找分号的一个或零个后跟,后跟1到5个数字(?:指定它是非捕获组,以便它不会单独出现在你的结果)

如果您希望它分别捕获端口和IP,您可以

def discover_device(in_str):
    m = re.search('(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:\:(\d{1,5}))?', in_str)
    if m:
        return (m.group(1), m.group(2))
    else:
        return None

Here's正则表达式,如果你想玩它。