正则表达式提取任意数量的子模式

Regex extract arbitrary number of subpatterns

我的句子结构为“名称有数字 1 字 1、数字 2 字 2、... 和数字 N 字 N”,其中子模式“数字字”的数量因句子而异,因此是不确定的。最后一个子模式前有一个“and”。例如“爱丽丝有 1 个苹果、2 个香蕉、....和 ​​6 个橙子。”

如何在 python 中使用正则表达式提取这些数字和单词?我希望输出如下:

姓名,

Digit Word
digit1 word1
digit2 word2
... ...
digitN wordN

我试过以下方法:

s = 'Alice has 1 apple, 2 bananas, and 3 oranges.'
import re
matches = re.finditer(r'([Aa-z]+) has (\d) ([a-z]+)( and)*', s)
for match in matches:
  print(match.groups())

但这只给我 ('Alice', '1', 'apple', None), 缺少 '2', 'bananas', '3', 'oranges'.

如果你想在一个正则表达式中匹配所有内容,你需要这样的东西:

([^\s]+) has (?:(?:,\s+)?(?:and\s+)?(\d+)\s+([^\s,]+)){1,}

Regex Demo

但是,我不确定 python 是否可以处理重复组。至少,我还没有找到从 python 对象中提取重复组的方法。

下面是我建议的解决问题的方法:

import re

s = 'Alice has 1 apple, 2 bananas, and 3 oranges.'

matches = re.match(r'^([^\s]+)', s)
print(f'Name: {matches.group(0)}')

matches = re.findall(r'(?:(?:,\s+)?(?:and\s+)?(\d+)\s+([^\s,]+))', s)

for match in matches:
    print(f'{match[0]} - {match[1]}')

示例输出

Name: Alice
1 - apple
2 - bananas
3 - oranges.

Process finished with exit code 0

正则表达式解释

^([^\s]+) - 有几种不同的方法可以解决这个问题,但它只是抓住所有内容,直到字符串中的第一个 space。

(?:           - Non-capturing group
 (?:,\s+)?    - Optionally allow the string to have a `,` followed by spaces
 (?:and\s+)?  - Optionally allow the string to contain the word `and` followed by spaces
 (\d+)        - Must have a number
 \s+          - Spaces between number and description
 ([^\s,]+)    - Grab the next set of characters and stop when you find a space or comma. This should be the word (e.g. apple)
)

这第二个正则表达式只是确保您可以提取各种形式的 1 apple。所以它基本上会匹配以下模式:

  • 1 apple
  • , 1 apple
  • , and 1 apple
  • and 1 apple

作为旁注,解析器更适合解决 long-运行 中的这些问题。句子中出现更多差异,使用简单的正则表达式开始变得非常难以解析。

使用PyPi regex.

See Python code:

import regex
s = 'Alice has 1 apple, 2 bananas, and 3 oranges.'
matches = regex.finditer(r'(?P<word1>[A-Za-z]+) has(?:(?:\s+|,\s+|,?\s+and\s+)?(?P<number>\d+)\s+(?P<word2>[a-z]+))*', s)
for match in matches:
  print(match.capturesdict())

结果{'word1': ['Alice'], 'number': ['1', '2', '3'], 'word2': ['apple', 'bananas', 'oranges']}