【问题标题】:How to determine the closest match to a regex pattern?如何确定与正则表达式模式最接近的匹配?
【发布时间】:2019-11-06 21:03:11
【问题描述】:

假设我有以下列表:

abc_Pickled_efg
abc_Pickller_efg
abcd_something_Picklesefgh
efg_Pickles_abc

还有正则表达式模式:

abc.*Pickles.*efg

列表中的这些都不匹配模式。但是我将如何找到最接近的匹配项(在本例中为 abc_Pickled_efg)。是否有一些 Python 模块或可以说明某事是 92% 匹配还是 0% 匹配或什么的?

我的想法是我可以遍历模式并查看每个符号是否匹配,即:

a  => a = 1 point
b  => b = 1 point
c  => c = 1 point
.* => _ = 1 point
P  => P = 1 point
i  => i = 1 point
c  => c = 1 point
k  => k = 1 point
l  => l = 1 point
e  => e = 1 point
r  => d = 0 point
.* => _ = 1 point
e  => e = 1 point
f  => f = 1 point
g  => g = 1 point
-----------------
         14 points out of 15 possible ~= 93% match

这有意义吗?或者有什么东西可以做这样的事情吗?

【问题讨论】:

  • 这能回答你的问题吗? Best Fuzzy Matching Algorithm?
  • 可以编写一个新的正则表达式引擎,通过特殊的回溯等来做到这一点。在哪里设置特殊选项Pickl?e?s?
  • 为什么abc_Pickled_efg 呈现最接近的匹配而不是abcd_something_Picklesefgh?您在第二个字符串中的匹配比在第一个字符串中更多。
  • @alfasin 是的,看起来就像我在这里尝试的一样
  • @ctwheels 你是对的。这只是我拼凑的一个例子,并没有仔细看,但认为我明白了我的意思。谢谢

标签: python regex


【解决方案1】:

如何在正则表达式中完成?

您可以使用具有内置模糊匹配技术的 PyPi 正则表达式模块。这种模糊性技术允许您根据正则表达式错误、允许的数量以及每种类型的模糊匹配错误的 score 来指定字符串上允许和不允许发生的情况。可以在here找到此信息。

就正则表达式而言,您将使用以下内容(显然,如果您在问题中指定的正则表达式是预先确定的,您可以将其连接起来;前置 (?: 并附加 ){e}):

(?:abc.*Pickles.*efg){e}

在上面的正则表达式中,{e} 在匹配字符串时允许任何类型的错误(删除、插入、替换)。您可以根据自己的喜好对其进行修改,下面列出了一些示例(许多示例来自我在第一段中提供的链接):

  • {d} 仅删除
  • {d,i} 仅删除和插入
  • {d,s,i} 删除、替换或插入(与 {e} 相同)- 顺序无关紧要
  • {e<=3} 最多允许 3 个错误
  • {i<3} 允许少于 3 次插入
  • {1<=e<3} 允许至少 1 个但少于 3 个错误
  • {1<=2,d>2} 最多允许 2 次插入,允许至少 2 次删除
  • {2i+d2+1s<=4}每次插入费用2,每次删除费用2,每次替换费用1,总费用不得超过4
  • {i<=1,d<=1,s<=1,2i+2d+1s<=4} 最多1次插入,最多1次删除,最多1次替换;每次插入成本 2,每次删除成本 2,每次替换成本 1,总成​​本不得超过 4

它的实现是什么样的?

现在您已经了解这是什么以及它是如何工作的,我们可以继续进行实际实施。下面的代码在计算中使用正则表达式模块的fuzzy_counts 来根据其分数确定最佳字符串。我的计算转换为百分比并按如下方式工作(您可以根据自己的喜好更改它,并结合上面的信息,更改正则表达式遇到的每种错误类型的分数):

# 100 * (1 - (fuzziness/length of original string))
100*(1 - (n/len(s)))

See this code working here

注意:我使用pprint 来很好地显示输出;没必要使用它,只是让结果更容易阅读。

import regex, pprint

strings = [
    "abc_Pickled_efg",
    "abc_Pickller_efg",
    "abcd_something_Picklesefgh",
    "efg_Pickles_abc"
]

r = r'(?:abc.*Pickles.*efg){e}'
matches = []

for i,s in enumerate(strings):
    x = regex.fullmatch(r,s, regex.BESTMATCH)   # match result
    n = sum(x.fuzzy_counts)             # fuzziness
    c = 100*(1 - (n/len(s)))            # closeness
    matches.append(c)

result = sorted(zip(strings, matches),key=lambda x: x[1],reverse=True)

pp = pprint.PrettyPrinter(indent=4)
pp.pprint(result)

结果:

[   ('abcd_something_Picklesefgh', 96.15384615384616),
    ('abc_Pickled_efg', 93.33333333333333),
    ('abc_Pickller_efg', 87.5),
    ('efg_Pickles_abc', 60.0)]

【讨论】:

  • 正是我想要的。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多