在使用正则表达式解析网络输出到组时遇到麻烦



我试图使用re.sub来解析sh ip bgp neighbor <neighbor> advertised-routes的输出。这可能是也可能不是这个任务的正确工具,但是pyats不能正确解析命令,所以我卡住了。

我没有编写解析器,而是计划收集原始输出,然后使用regex将输出分隔成可用于格式化数据的字段。

所以我有问题的部分是:

test_string = " *>   0.0.0.0          192.168.232.89                         0 209 65000 i"
new_string = re.sub('*>s+(S+)s+', '(S+)s{1,25}.s{3,}', 's+(.+?(?=[i?]))', r'1', test_string)
print(test_string)

我得到以下错误:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:UsersmupchAppDataLocalProgramsPythonPython310libre.py", line 209, in sub
return _compile(pattern, flags).sub(repl, string, count)
File "C:UsersmupchAppDataLocalProgramsPythonPython310libre.py", line 303, in _compile
p = sre_compile.compile(pattern, flags)
File "C:UsersmupchAppDataLocalProgramsPythonPython310libsre_compile.py", line 764, in compile
p = sre_parse.parse(p, flags)
File "C:UsersmupchAppDataLocalProgramsPythonPython310libsre_parse.py", line 948, in parse
p = _parse_sub(source, state, flags & SRE_FLAG_VERBOSE, 0)
TypeError: unsupported operand type(s) for &: 'str' and 'int'

我在regex101.com上测试了以下正则表达式,以验证我正确地分离了它:

*>s+(S+)s+(S+)s{1,25}.s{3,}[0-9]s+(.+?(?=[i?]))

这与我想要的组相匹配,目标是只看到组123并消除中间的垃圾。

任何帮助都将是感激的。

我不知道为什么,但是你用5个不匹配函数签名的参数调用re.sub()。来自文档:

re.sub(pattern, repl, string, count=0, flags=0)

传递的不是单个pattern,而是三个独立的模式。因此,它试图将第4个参数(字符串r'1')解释为一个数字,因为这就是它所期望的(即count,默认值为0)。这就是你收到TypeError的原因。


相反,将模式作为单个参数传递:

>>> test_string
' *>   0.0.0.0          192.168.232.89                         0 209 65000 i'
>>> regex
'\*>\s+(\S+)\s+(\S+)\s{1,25}.\s{3,}[0-9]\s+(.+?(?=[i\?]))'
>>> re.sub(regex, r'1, 2, 3', test_string)
' 0.0.0.0, 192.168.232.89, 209 65000 i'

另外,您可以使用re.search(),而不是使用re.sub(),它返回一个Match对象,这使您更灵活地以编程方式处理结果:

import re
TEST_STRING=r' *>   0.0.0.0          192.168.232.89                         0 209 65000 i'
REGEX=r'*>s+(S+)s+(S+)s{1,25}.s{3,}[0-9]s+(.+?(?=[i?]))'
result = re.search(REGEX, TEST_STRING)
print("Accessing matches by index...")
full_match = result[0]
group1 = result[1]
group2 = result[2]
group3 = result[3]
print(f'Full match: "{full_match}"ngroup 1: "{group1}"ngroup 2: "{group2}"ngroup 3: "{group3}"')
print("nGetting matches in a tuple...")
groups = result.groups()
for index, group in enumerate(groups, 1):
print(f'group {index}: "{group}"')

输出:

$ python3 groups.py
Accessing matches by index...
Full match: "*>   0.0.0.0          192.168.232.89                         0 209 65000 "
group 1: "0.0.0.0"
group 2: "192.168.232.89"
group 3: "209 65000 "
Getting matches in a tuple...
group 1: "0.0.0.0"
group 2: "192.168.232.89"
group 3: "209 65000 "

当使用re.sub()时,如果您想要丢弃不包含在其中一个组中的文本,您必须指定一个捕获所有文本的模式。否则,将保留模式前后的文本。

下面的脚本演示了:

import re
TEST_STRING=r' *>   0.0.0.0          192.168.232.89                         0 209 65000 i'
REGEX=r'*>s+(S+)s+(S+)s{1,25}.s{3,}[0-9]s+(.+?(?=[i?]))'
sub_result = re.sub(REGEX, r'group 1: "1", group 2: "2", group 3: "3"', TEST_STRING)
print(f'REGEX result:n'{sub_result}'')
MATCH_ALL_REGEX=r'.**>s+(S+)s+(S+)s{1,25}.s{3,}[0-9]s+(.+?(?=[i?])).*'
sub_result = re.sub(MATCH_ALL_REGEX, r'group 1: "1", group 2: "2", group 3: "3"', TEST_STRING)
print(f'MATCH_ALL_REGEX result:n'{sub_result}'')

输出:

$ python3 solution.py
REGEX result:
' group 1: "0.0.0.0", group 2: "192.168.232.89", group 3: "209 65000 "i'
MATCH_ALL_REGEX result:
'group 1: "0.0.0.0", group 2: "192.168.232.89", group 3: "209 65000 "'

请注意,第二个结果既不包含前导空格,也不包含末尾的&;i&;。

这个答案解决了在"路由"上匹配的请求。还有"路径"(或"weight_path"因为我相信前导零是一个"权重")AND同时将这些组赋值给变量

我们可以利用re.compile()方法以及"命名组"。生成包含键值对/变量的组字典。下面演示了使用两个不同的数据字符串的结果,以显示模式与命名组结合使用的多功能性。

示例中提供的默认路由将用作最简单的示例——没有前缀长度和度量:

test_string = " *>   0.0.0.0          192.168.232.89                         0 209 65000 i"

包含前缀长度和度量的非默认路由也用于提供使用完全相同模式的更真实的示例:

tst_data = "*>i154.68.9.128/32     10.0.161.2                      100      0 64531 64539 64539 64539 64539 64539 64539 64539 64539 ?"

这里是带有命名组的模式(pat), (?Psome-pattern),用于

  • "route">
  • "next_hop">
  • "metric">
  • "weight_path">

后面跟着结果输出:

>>> from re import compile
>>> 
>>> pat = compile(
...     r".+?(?P<route>"         # name for first named group
...      "(d{1,3}.){3}"        # 3 groups of 1 to 3 digits followed by a dot
...      "d{1,3}(/d{1,2})?)"   # 1 to 3 digits + optional slash + prefix length
...      "s+(?P<next_hop>"      # name for second named group
...      "(d{1,3}.){3}"        # 3 groups of 1 to 3 digits followed by a dot
...      "d{1,3})"              # 1 to 3 digits
...      "s+(?P<metric>"        # name for third named group
...      "d+(?=s{2,}))?"       # 1 or more digits + at least 2 spaces; look ahead
...      "s+(?P<weight_path>"   # name for fourth named group
...      "d+(s?d+)+)"         # 1 or more digits + optional space + more digits
... )
>>> 
>>> test_string = " *>   0.0.0.0          192.168.232.89                         0 209 65000 i"
>>> data = pat.match(test_string)
>>> data['route']
'0.0.0.0'
>>> data['next_hop']
'192.168.232.89'
>>> data['metric']
>>> data['weight_path']
'0 209 65000'
>>> 
>>> 
>>> tst_data = "*>i154.68.9.128/32     10.0.6.2                      100      0 64531 64539 64539 64539 64539 64539 64539 64539 64539 ?"
>>> data = pat.match(tst_data)
>>> data['route']
'154.68.9.128/32'
>>> data['next_hop']
'10.0.161.2'
>>> data['metric']
'100'
>>> data['weight_path']
'0 64531 64539 64539 64539 64539 64539 64539 64539 64539'
>>> 

"data"变量是一个re.Match对象,该对象包含一个"groupdict"属性,上面演示过,下面详细说明。

>>> data.groupdict()  # when data = pat(test_string)
{'route': '0.0.0.0', 'next_hop': '192.168.232.89', 'metric': None, 'weight_path': '0 209 65000'}
>>> 

>>> data.groupdict()  # when data = pat(tst_data)
{'route': '154.68.9.128/32', 'next_hop': '10.0.161.2', 'metric': '100', 'weight_path': '0 64531 64539 64539 64539 64539 64539 64539 64539 64539'}
>>> 

命名组生成键-值对的字典,这是真正的"魔法",然后我们就能够以所需的任何格式排列数据。

>>> for key in data.groupdict():
...     print(f"key: {key:<11}  --  {data[key]}")
... 
key: route        --  154.68.9.128/32
key: next_hop     --  10.0.161.2
key: metric       --  100
key: weight_path  --  0 64531 64539 64539 64539 64539 64539 64539 64539 64539
>>> 

最新更新