我如何编写 Python 正则表达式,它将采用 4 个数字后跟拼音字母值?示例:1 2 3 4 Alpha Bravo -> 1234AB

问题描述

我正在使用以下脚本,以便 Rasa 框架在用户传递荷兰邮政编码时检测到:

https://medium.com/@naoko.reeves/rasa-regex-entity-extraction-317f047b28b6

荷兰邮政编码的格式是 1234 AB。这在使用正则表达式时非常有效,例如:

 [1-9][0-9]{3}[\s]?[a-z]{2}

但是,我现在正在尝试实现语音转文本功能(Azure 认知服务),该功能不太容易识别字母表。例如“B”被选为“Bee”。

我现在正在尝试更改正则表达式,以便用户可以说“1 2 3 4 Alpha Bravo”,而正则表达式提取器会选择“1 2 3 4 A B”。

我尝试过使用如下的词边界:

[1-9]*[\s]?[0-9]*[\s]?[0-9]*[\s]?[0-9]*[\s]?\b[a-zA-Z]

[1-9]\s[0-9\s]{5}\s?\b[a-zA-Z]

前者过于宽松,如果用户说“你好”,它将触发正则表达式提取器并将“HT”传递给邮政编码行为。

后者更严格,但我只能得到 '1 2 3 4 Alpha Bravo' 匹配为 '1 2 3 4 A'。

对于如何解决此问题的任何解决方案,我都非常感激。如果这在 Regex 中不容易实现,我相信在链接的中等文章中更改以下函数会得到我想要的结果。不幸的是,我不是 Python/Regex 专家:)

 def match_regex(self,message):
    extracted = []
    for d in self.regex_feature:
        match = re.search(pattern=d['pattern'],string=message)
        if match:
            entity = {
                "start": match.pos,"end": match.endpos,"value": match.group(),"confidence": 1.0,"entity": d['name'],}
            extracted.append(entity)
    extracted = self.add_extractor_name(extracted)
    return extracted

我希望这已经足够清楚了。

谢谢!

杰克

解决方法

也许你可以试试这个正则表达式:

(?i)\b([1-9][0-9]{3} ?[a-z])[a-z]* +([a-z])[a-z]*

无论与此正则表达式匹配的内容,只需将其替换为 \1\2,即 Group1 的内容后跟 Group 2 的内容。

Click for Demo

Click for Code

说明:

  • (?i) - 切换使匹配不区分大小写
  • \b - 一个词边界
  • ([1-9][0-9]{3} ?[a-z]) - 组 1 的内容如下所述
    • [1-9] - 匹配从 1 到 9 的任何数字
    • [0-9]{3} - 匹配 3 次从 0 到 9 的任何数字
    • ? - 匹配 0 或 1 个空格
    • [a-z] - 匹配单个出现的字母。 这将是数字后第一个单词的第一个字母
  • [a-z]* - 匹配 0+ 个字母
  • + - 匹配 1+ 个空格
  • ([a-z]) - 匹配一个字母并将其存储在 Group 2 中。 这将是第二个单词的第一个字母
  • [a-z]* - 匹配 0+ 个字母
,

您可以使用 3 组匹配数字之间和大写字符 A-Z 之间的可选空格。

([1-9](?:\s*[0-9]){3})\s?([A-Z])[a-z]*\s*([A-Z])[a-z]*

模式匹配

  • ([1-9](?:\s*[0-9]){3}) 匹配 4 位数字和可选的空白字符
  • \s? 匹配一个可选的空格
  • ([A-Z])[a-z]*\s* 匹配一个大写字符 A-Z 后跟可选的小写字符和可选的 whitespac
  • ([A-Z])[a-z]* 匹配大写字符 A-Z 后跟可选的小写字符

regex demo

更严格的选项可能是匹配大写字符 A-Z 后跟仅使用可选重复反向引用的相同字符的大写或小写变体

\b([1-9](?:\s*[0-9]){3})\s?([A-Z])(?i:\2*)\s*([A-Z])(?i:\3*)\b

Regex demo | Python demo

import re

pattern = r"\b([1-9](?:\s*[0-9]){3})\s?([A-Z])(?i:\2*)\s*([A-Z])(?i:\3*)\b"
strings = [
    "1 2 3 4 Alpha Bravo","1234 Alpha Bravo","1234A Bbbbbbbc","1234Aaa Bbb","1234Aa Bbb","1234A BbbbbBbb"
]

for s in strings:
    print(re.findall(pattern,s))

输出

[]
[]
[]
[('1234','A','B')]
[('1234','B')]