首页 > 解决方案 > 在 Python 中使用原子分组的最佳方式?

问题描述

所以我写了这个,这太可怕了:

def parse_results(string):
    space = r"([\s\t]{0,5})"
    building_type = r"(([Uu]nit|[Ss]tudio|[Ff]lat)?)"
    street_type = (r"((\d+)(\&|\-)*(\d*)(\w*)(\w*)(\s*)(\w*)(\s*)(\w*)(\s*)"
                   r"([Ee]nd|[Gg]reen|[Cc]auseway|[Cc]heapside|[Cc]rescent|"
                   r"[Ss]treet|[Ll]ane|[Ww]alk|[Rr]oad|[Aa]venue|[Dd]rive|"
                   r"[Pp]ark|[Ww]ay|[Pp]lace|[Pp]arade|[Ii]ndustrial"
                   r"[Ee]state|[Tt]rading [Ee]state|[Hh]ouse|[Gg]reen))")
    line_1 = r"(\w*)"
    line_2 = r"(\w*)"
    line_3 = r"(\w*)"
    line_4 = r"(\w*)"
    line_5 = r"(\w*)"
    postcode = r"(([A-Z0-9][A-Z0-9][A-Z0-9]?[A-Z0-9]? {1,2}[0-9][A-Z]{2}))"
    pattern = re.compile(rf"({building_type}{space}{street_type}{space}"
                         rf"{line_1}{space}{line_2}{space}{line_3}{space}"
                         rf"{line_4}{space}{line_5}{space}{postcode})")
    try:
        matches = pattern.finditer(string)
        for match in matches:
            address = re.sub(r"\s+", r" ", match.group(1))
        return address
    except Exception as e:
        return (f"Error looking for address, exception {e}")

它的目的是在我用于机器学习培训的大型文本语料库中寻找英国地址。然而,由于回溯,它非常缓慢。

经过研究,解决方案似乎是使用原子分组,类似于在 Ruby 中完成的方式。

Python RE 模块不支持开箱即用,但是有一些解决方法,例如:

Python 正则表达式是否等同于 Ruby 的原子分组?

显然,有一个 Python Regex模块确实支持开箱即用的 atomix 分组,但几乎没有人在教程中谈论它。

两个问题:

  1. 哪个是最好的方法?RE 模块变通还是 Regex 模块?

  2. 有人可以指出示例的方向,以便我可以为我的用例解决这个问题吗?

谢谢!

标签: pythonpython-3.xregex

解决方案


无论您是使用re还是regex,您都必须修复您的模式,因为它很容易发生灾难性的回溯。这里不需要原子分组,您需要具有强制性模式的可选分组。此外,您需要修复可能在字符串内的同一位置开始匹配的交替。

您可以使用

(?:[Uu]nit|[Ss]tudio|[Ff]lat)?\s{0,5}\d+[&-]*\w*\s*(?:[Ee]nd|[Gg]reen|[Cc]auseway|[Cc]heapside|[Cc]rescent|[Ss]treet|[Ll]ane|[Ww]alk|[Rr]oad|[Aa]venue|[Dd]rive|[Pp]ark|[Ww]ay|[Pp]lace|[Pp]arade|[Ii]ndustrial[Ee]state|[Tt]rading [Ee]state|[Hh]ouse|[Gg]reen)(?:\s{1,5}\w+){0,5}\s{0,5}[A-Z0-9][A-Z0-9][A-Z0-9]?[A-Z0-9]? {1,2}[0-9][A-Z]{2}

请参阅正则表达式演示

请参阅Python 演示

import re
 
def parse_results(string):
    space = r"\s{0,5}"
    building_type = r"(?:[Uu]nit|[Ss]tudio|[Ff]lat)?"
    street_type = (r"\d+[&-]*\w*\s*"
                   r"(?:[Ee]nd|[Gg]reen|[Cc]auseway|[Cc]heapside|[Cc]rescent|"
                   r"[Ss]treet|[Ll]ane|[Ww]alk|[Rr]oad|[Aa]venue|[Dd]rive|"
                   r"[Pp]ark|[Ww]ay|[Pp]lace|[Pp]arade|[Ii]ndustrial"
                   r"[Ee]state|[Tt]rading [Ee]state|[Hh]ouse|[Gg]reen)")
    postcode = r"[A-Z0-9][A-Z0-9][A-Z0-9]?[A-Z0-9]? {1,2}[0-9][A-Z]{2}"
    pattern = re.compile(rf"{building_type}{space}{street_type}(?:\s{{1,5}}\w+){{0,5}}"
                         rf"{space}{postcode}")
    print(pattern.pattern)
    try:
        return [re.sub(r"\s+", r" ", x) for x in pattern.findall(string)]
    except Exception as e:
        return (f"Error looking for address, exception {e}")
 
print(parse_results('Unit 23 End AS4 0SS'))

推荐阅读