从 Python 3.x 中的列表中提取 IP 和端口
Extract IPs and Ports from a list in Python 3.x
我想从返回的列表中提取 IP 和端口。我目前正在使用 str(var).replace 命令删除多余的字符。这 will/has 在字符串格式更改时导致问题,使 .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 和端口而不必依赖周围字符的完整性的最佳做法是什么?
来自:[<Device: 192.168.222.123:8075>]
为此:192.168.222.123:8075
最好是:[192.168.222.123, 8075]
考虑到点块内的 IP 差异和基于 16 位的最大端口号(通常冒号后 4 个整数,最多 5 个整数)
您可以简单地使用 regex 来查找 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']
为此不需要正则表达式。使用 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')
我认为最好的办法是使用正则表达式:
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 正则表达式,如果你想玩的话。
假设一个 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 地址。如果找到 none,则会引发异常。
尽管验证端口号不在问题范围内,但假定端口是一个数字。 validate_port
装饰器中添加了一个简单的测试,可以根据需要应用或更新。装饰器筛选来自 discover_device()
的输出。如果端口不是纯数字,则会引发异常。有关 Python 装饰器的精彩教程,请参阅 this post for modifying restrictions. See 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 值传递给后一个函数,即 return validate_port(result[1:])
和 return validate_port(result)
。
向@coder 提出建议。
我想从返回的列表中提取 IP 和端口。我目前正在使用 str(var).replace 命令删除多余的字符。这 will/has 在字符串格式更改时导致问题,使 .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 和端口而不必依赖周围字符的完整性的最佳做法是什么?
来自:[<Device: 192.168.222.123:8075>]
为此:192.168.222.123:8075
最好是:[192.168.222.123, 8075]
考虑到点块内的 IP 差异和基于 16 位的最大端口号(通常冒号后 4 个整数,最多 5 个整数)
您可以简单地使用 regex 来查找 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']
为此不需要正则表达式。使用 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')
我认为最好的办法是使用正则表达式:
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 正则表达式,如果你想玩的话。
假设一个 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 地址。如果找到 none,则会引发异常。
尽管验证端口号不在问题范围内,但假定端口是一个数字。 validate_port
装饰器中添加了一个简单的测试,可以根据需要应用或更新。装饰器筛选来自 discover_device()
的输出。如果端口不是纯数字,则会引发异常。有关 Python 装饰器的精彩教程,请参阅 this post for modifying restrictions. See 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 值传递给后一个函数,即 return validate_port(result[1:])
和 return validate_port(result)
。
向@coder 提出建议。