匹配两个反向断言中的一个

3

我正在尝试从日志文件中提取设备的id,并将其填充到 Pandas.DataFrame 中的一列中。

问题在于,id 可能会被两种不同的模式所包含,如下所示:

模式1:

(?<=cameraId=\')([a-z0-9-]+))

模式2:

(?<=/live/)([a-z0-9-]+)

注意: 一行中不可能同时存在这两种模式。

问题是我使用了 Pandas.String.str.findall() 方法,我想让这两种模式都匹配到。

如下代码所示,我可以成功实现期望的结果:

import pandas as pd

line_1 = 'INFO:2021-04-19 00:25:10,647:instance_manager.py:MainProcess:1:got event notificationName=\'DETECTION_STARTED\' cameraId=\'ab1c-ab6c-a6f6-a6d6-ab666\' timestamp=\'2021-04-19T00:24:08.192169Z\''

line_2 = 'INFO:2021-04-19 00:25:11,278:instance_manager.py:MainProcess:1:An old record record for the stream rtsp://127.0.1.1:6666/live/a001-a00a-0016-a006-ab606.stream was successfully updated in the DB!'

df = pd.DataFrame(columns=['type', 'ts', 'process', 'subprocess', 'line', 'message'])

line_1_parsed = pd.Series([line_1]).str.extract(r'(?P<type>[^:]+):(?P<ts>.+,\d+):(?P<process>[^:]+):(?P<subprocess>[^:]+):(?P<line>[^:]+):(?P<message>[^$]+)')
line_2_parsed = pd.Series([line_2]).str.extract(r'(?P<type>[^:]+):(?P<ts>.+,\d+):(?P<process>[^:]+):(?P<subprocess>[^:]+):(?P<line>[^:]+):(?P<message>[^$]+)')

df =df.append(line_1_parsed, ignore_index=True)
df =df.append(line_2_parsed, ignore_index=True)

df.loc[:, 'cam_id'] = df.loc[:, 'message'].str.findall('(?<=cameraId=\')([a-z0-9-]+)|(?<=/live/)([a-z0-9-]+)')
df

但它们返回的是元组形式(pattern 1, pattern 2),如下所示:

现有输出:

    type    ts  process     subprocess  line    message     cam_id
0   INFO    2021-04-19 00:25:10,647     instance_manager.py     MainProcess     1   got event notificationName='DETECTION_STARTED'...   [(ab1c-ab6c-a6f6-a6d6-ab666, )]
1   INFO    2021-04-19 00:25:11,278     instance_manager.py     MainProcess     1   An old record record for the stream rtsp://127...   [(, a001-a00a-0016-a006-ab606)]

我明白这是由于它尝试了两种模式并返回了两者的匹配结果,但我更希望只有成功的模式。

当然,我可以通过手动提取来实现:

df.loc[:, 'cam_id'] = df.loc[:, 'cam_id'].apply(lambda cam_id_tuple: cam_id_tuple[0][0] if cam_id_tuple[0][0] != '' else cam_id_tuple[0][1])
df

但这是一种比较繁琐的解决方法,如果我想添加模式,它也不具备可扩展性。

    type    ts  process     subprocess  line    message     cam_id
0   INFO    2021-04-19 00:25:10,647     instance_manager.py     MainProcess     1   got event notificationName='DETECTION_STARTED'...   [ab1c-ab6c-a6f6-a6d6-ab666]
1   INFO    2021-04-19 00:25:11,278     instance_manager.py     MainProcess     1   An old record record for the stream rtsp://127...   [a001-a00a-0016-a006-ab606]`


注意:`cam_id` 列包含字符串而不是元组。
提前感谢。
2个回答

4
我们可以使用带有单个捕获组的正则表达式模式在pandas.Series.str.extract中进行操作。
df['message'].str.extract(r'(?:cameraId=\'|/live/)([a-z0-9-]+)', expand=False)

0    ab1c-ab6c-a6f6-a6d6-ab666
1    a001-a00a-0016-a006-ab606
Name: message, dtype: object

正则表达式详解:

  • (?:cameraId=\'|/live/):非捕获型组
    • cameraId=\':第一种匹配项必须匹配字符 cameraId='
    • /live/:第二种匹配项必须匹配字符 /live/
  • ([a-z0-9-]+):第一个捕获型组
    • [a-z0-9-]+:匹配列表 [a-z0-9-] 中的任意字符,至少出现一次

请参见在线正则表达式演示


3

根据您提供的示例,您也可以尝试以下函数。

df['message'].str.extract(r'.*(?:live\/|cameraId=\')([^\'.]*)', expand=False)

以上代码的输出结果将是:

0   ab1c-ab6c-a6f6-a6d6-ab666
1   a001-a00a-0016-a006-ab606

这里是上述代码的在线演示

说明:对上述内容进行详细解释。

.*(?:live\/|cameraId=\')  ##From starting match till live/ OR cameraId=' in a non-captuging group.
([^\'.]*)                 ##Creating 1ct capturing group and matching until ' OR . here.

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