【问题标题】:How to capture all repitions of a subpattern in regex如何在正则表达式中捕获子模式的所有重复
【发布时间】:2021-11-30 00:43:13
【问题描述】:

我有一个格式化的字符串,它可以有任意长度的重复部分。例如,这是我想要解析的元数据示例。

File Name: chb03_34.edf
File Start Time: 01:51:23
File End Time: 2:51:23
Number of Seizures in File: 1
Seizure Start Time: 1982 seconds
Seizure End Time: 2029 seconds

File Name: chb23_07.edf
File Start Time: 11:03:16
File End Time: 11:45:56
Number of Seizures in File: 0

File Name: chb23_08.edf
File Start Time: 11:48:05
File End Time: 14:40:27
Number of Seizures in File: 2
Seizure 1 Start Time: 325 seconds
Seizure 1 End Time: 345 seconds
Seizure 2 Start Time: 5104 seconds
Seizure 2 End Time: 5151 seconds

File Name: chb23_09.edf
File Start Time: 14:40:47
File End Time: 18:41:13
Number of Seizures in File: 4
Seizure 1 Start Time: 2589 seconds
Seizure 1 End Time: 2660 seconds
Seizure 2 Start Time: 6885 seconds
Seizure 2 End Time: 6947 seconds
Seizure 3 Start Time: 8505 seconds
Seizure 3 End Time: 8532 seconds
Seizure 4 Start Time: 9580 seconds
Seizure 4 End Time: 9664 seconds

到目前为止,我已经创建了一个正则表达式,它可以捕获第一行,但只能在一个块中捕获最后一个癫痫发作(如果存在癫痫发作)。

import re

summary = "a formatted string read"


pattern = "File Name\: (.+)\nFile Start Time\: (.+)\nFile End Time\: (.+)\nNumber of Seizures in File\: (.+)(?:\n|\r|)(?:Seizure(?: | \d )Start Time\: (\d+) seconds\nSeizure(?: | \d )End Time\: (\d+) seconds(?:\n|\r|))*"
pattern = re.compile(pattern)

for p in pattern.finditer(summary):
    print(p.groups())

但是,例如最后一个块的这种模式的结果将仅捕获癫痫发作 4 的开始和结束时间。是否可以递归捕获重复的子模式?

编辑:使用regex 和模式The fourth bird 已在 cmets 中键入,我可以匹配字符串,但我在重复行中得到很多 None 值,也完全是 None 行。我怎样才能摆脱这些,或插入适当的值?

('chb23_06.edf', '08:57:57', '11:02:43', '1', '3962', '4075')
(None, None, None, None, None, None)
('chb23_07.edf', '11:03:16', '11:45:56', '0', None, None)
(None, None, None, None, None, None)
('chb23_08.edf', '11:48:05', '14:40:27', '2', '325', '345')
(None, None, None, None, '5104', '5151')
(None, None, None, None, None, None)
('chb23_09.edf', '14:40:47', '18:41:13', '4', '2589', '2660')
(None, None, None, None, '6885', '6947')
(None, None, None, None, '8505', '8532')
(None, None, None, None, '9580', '9664')
(None, None, None, None, None, None)
('chb23_10.edf', '18:41:40', '22:41:40', '0', None, None)
(None, None, None, None, None, None)
('chb23_16.edf', '13:46:32', '17:46:32', '0', None, None)
(None, None, None, None, None, None)
('chb23_17.edf', '17:46:42', '21:16:29', '0', None, None)
(None, None, None, None, None, None)
('chb23_19.edf', '02:28:28', '6:28:28', '0', None, None)
(None, None, None, None, None, None)
('chb23_20.edf', '06:28:36', '7:52:05', '0', None, None)
(None, None, None, None, None, None)

EDIT2:我完成了先前接受的答案的解决方案,但它有一些粗糙的边缘并且在某些文件中不起作用。我上传了一个有问题的文件。您可以在 here 中找到有问题的元数据示例的粘贴。

【问题讨论】:

  • 由于模式中 | 的交替,您会得到 None 值。您可以从结果中过滤 None 值,或者您可以使用不同的方法,通过使用初始模式,并在包含 Seizure 值的单个组中捕获最后的所有重复行,然后对该组使用 split 到获取单独的值。
  • 您应该在问题中实际“运行”正则表达式的位置添加代码。
  • @FarhoodET 我认为this approach 更容易
  • 您是否考虑过只逐行读取文件而不是正则表达式并以这种方式构建数据集?尽管您似乎在这两个答案之间有自己的工作答案。
  • @Jarvis 是的,但这种方式更难将每个文件的元数据实际拼凑起来。我现在接受的答案是完全可以的。

标签: python regex string


【解决方案1】:

使用re,您可以捕获组中Seizure 字符串的可选迭代,然后从该组中捕获秒的数字值:

图案

File Name: (.+)\nFile Start Time: (.+)\nFile End Time: (.+)\nNumber of Seizures in File: (.+)((?:\nSeizure (?:\d )?Start Time: \d+ seconds\nSeizure (?:\d )?End Time: \d+ seconds)*)

模式匹配:

  • File Name: (.+)\n 第 1 组,匹配文件名之后的所有内容:和换行符
  • File Start Time: (.+)\n 第 2 组,匹配文件开始时间之后的所有内容:和换行符
  • File End Time: (.+)\n 第 3 组,匹配文件结束时间之后的所有内容:和换行符
  • Number of Seizures in File: (.+) 第 4 组,在 Number of Seizures in File 之后匹配所有内容:
  • ( 第 5 组
    • (?:非捕获组作为一个整体匹配,然后可选地重复
      • \nSeizure (?:\d )?Start Time: \d+ seconds\n 匹配换行符并匹配 Seizure Start Time 和末尾的换行符
      • Seizure (?:\d )?End Time: \d+ seconds匹配扣押结束时间
    • )*关闭非捕获组并选择性地重复它
  • )关闭第5组

Regex demo | Python demo

例如

pattern = re.compile(pattern)

for m in pattern.finditer(summary):
    print(m.group(1))
    print(m.group(2))
    print(m.group(3))
    print(m.group(4))
    print(re.findall(r"(\d+) seconds", m.group(5)))

每场比赛的输出如下所示:(或在没有 Seizure 值时为空列表,但您也可以对其进行测试)

chb23_08.edf
11:48:05
14:40:27
2
['325', '345', '5104', '5151']

【讨论】:

  • 很抱歉再次打扰您,这篇文章中给出的答案确实不适用于我拥有的所有元数据,并且我遇到了一些有问题的文件,我再次编辑了我的帖子。如果您能再看一遍,我将不胜感激。
  • @FarhoodET 似乎出现了多个空格。请参阅此正则表达式以获取第一个匹配 regex101.com/r/p5zyE8/1 和此正则表达式以获取第二个匹配 regex101.com/r/CV2umn/1 以匹配一个或多个空白字符。
  • @FarhoodET 使用\s+ 也可以匹配换行符,如果你只想匹配没有换行符的空白字符,你可以使用[^\S\n]+ 代替
  • 那么我应该改变哪些位置?我应该将所有空格都更改为多个吗?
  • @FarhoodET 如果您知道某些部分只有一个空格,那么您不必更改这些位置。请参阅此示例更改所有位置ideone.com/bOMUVu
【解决方案2】:

如果您使用的是regex 模块,我建议您使用重复捕获。

为了清楚起见,我还添加了命名组:

import regex

pattern = regex.compile(
    r"File Name: (?P<name>.+)\n"
    r"File Start Time: (?P<start>.+)\n"
    r"File End Time: (?P<end>.+)\n"
    r"Number of Seizures in File: (?P<count>\d+)\n"
    r"(?:\n|(?:Seizure (?:\d )?Start Time: (?P<seizure_start>\d+) seconds\n"
    r"Seizure (?:\d )?End Time: (?P<seizure_end>\d+) seconds\n)*)"
)

summary = """File Name: chb03_34.edf
File Start Time: 01:51:23
File End Time: 2:51:23
Number of Seizures in File: 1
Seizure Start Time: 1982 seconds
Seizure End Time: 2029 seconds

File Name: chb23_07.edf
File Start Time: 11:03:16
File End Time: 11:45:56
Number of Seizures in File: 0

File Name: chb23_08.edf
File Start Time: 11:48:05
File End Time: 14:40:27
Number of Seizures in File: 2
Seizure 1 Start Time: 325 seconds
Seizure 1 End Time: 345 seconds
Seizure 2 Start Time: 5104 seconds
Seizure 2 End Time: 5151 seconds

File Name: chb23_09.edf
File Start Time: 14:40:47
File End Time: 18:41:13
Number of Seizures in File: 4
Seizure 1 Start Time: 2589 seconds
Seizure 1 End Time: 2660 seconds
Seizure 2 Start Time: 6885 seconds
Seizure 2 End Time: 6947 seconds
Seizure 3 Start Time: 8505 seconds
Seizure 3 End Time: 8532 seconds
Seizure 4 Start Time: 9580 seconds
Seizure 4 End Time: 9664 seconds
"""

for match in pattern.finditer(summary):
    print("Name:", match.group("name"))
    print("Seizure Count", match.group("count"))
    seizures = tuple(
        zip(match.captures("seizure_start"),match.captures("seizure_end")))
    for i, (start, end) in enumerate(seizures, start=1):
        print(f"Seizure #{i}: {start} -> {end}")

打印:

Name: chb03_34.edf
Seizure Count 1
Seizure #1: 1982 -> 2029
Name: chb23_07.edf
Seizure Count 0
Name: chb23_08.edf
Seizure Count 2
Seizure #1: 325 -> 345
Seizure #2: 5104 -> 5151
Name: chb23_09.edf
Seizure Count 4
Seizure #1: 2589 -> 2660
Seizure #2: 6885 -> 6947
Seizure #3: 8505 -> 8532
Seizure #4: 9580 -> 9664

【讨论】:

  • 嗨。很抱歉再次打扰您,但您给我的解决方案并不完全有效。我上传了一个示例文件供您查看,您可以在编辑后的帖子中找到它。
猜你喜欢
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-06
  • 2022-01-23
相关资源
最近更新 更多