我正在从两个不同的来源录制一些文字。他们每个人都可以在不同的地方犯错,他们不会识别一封信/一组信件。如果他们不认识某事,那就换成了?例如,如果单词为Roflcopter
,则一个来源可能会返回Ro?copter
,而另一个来源可能会返回Roflcop?er
。我想要一个函数来返回两个匹配是否相等,允许多个?
s。例如:
match("Ro?copter", "Roflcop?er") --> True
match("Ro?copter", "Roflcopter") --> True
match("Roflcopter", "Roflcop?er") --> True
match("Ro?co?er", "Roflcop?er") --> True
到目前为止,我可以使用正则表达式将一个OCR与一个完美的OCR匹配:
>>> def match(tn1, tn2):
tn1re = tn1.replace("?", ".{0,4}")
tn2re = tn2.replace("?", ".{0,4}")
return bool(re.match(tn1re, tn2) or re.match(tn2re, tn1))
>>> match("Roflcopter", "Roflcop?er")
True
>>> match("R??lcopter", "Roflcopter")
True
但是当它们都在不同的地方时,这不起作用:
>>> match("R??lcopter", "Roflcop?er")
False
答案 0 :(得分:2)
def match(str1, str2):
if len(str1) != len(str2): return False
for index, ch1 in enumerate(str1):
ch2 = str2[index]
if ch1 == '?' or ch2 == '?': continue
if ch1 != ch2: return False
return True
>>> ================================ RESTART ================================
>>>
>>> match("Roflcopter", "Roflcop?er")
True
>>> match("R??lcopter", "Roflcopter")
True
>>>
>>> match("R??lcopter", "Roflcop?er")
True
>>>
编辑:B部分),脑屁现在免费。
def sets_match(set1, set2):
return any(match(str1, str2) for str1 in set1 for str2 in set2)
>>> ================================ RESTART ================================
>>>
>>> s1 = set(['a?', 'fg'])
>>> s2 = set(['?x'])
>>> sets_match(s1, s2) # a? = x?
True
>>>
答案 1 :(得分:2)
感谢Hamish Grubijan的这个想法。每个?在我的ocr'd名称中可以是0到3个字母。我所做的是将每个字符串扩展为可能的扩展列表:
>>> list(expQuestions("?flcopt?"))
['flcopt', 'flcopt@', 'flcopt@@', 'flcopt@@@', '@flcopt', '@flcopt@', '@flcopt@@', '@flcopt@@@', '@@flcopt', '@@flcopt@', '@@flcopt@@', '@@flcopt@@@', '@@@flcopt', '@@@flcopt@', '@@@flcopt@@', '@@@flcopt@@@']
然后我展开两个并使用他的匹配函数,我称之为matchats
:
def matchOCR(l, r):
for expl in expQuestions(l):
for expr in expQuestions(r):
if matchats(expl, expr):
return True
return False
按需运作:
>>> matchOCR("Ro?co?er", "?flcopt?")
True
>>> matchOCR("Ro?co?er", "?flcopt?z")
False
>>> matchOCR("Ro?co?er", "?flc?pt?")
True
>>> matchOCR("Ro?co?e?", "?flc?pt?")
True
<小时/> 匹配功能:
def matchats(l, r):
"""Match two strings with @ representing exactly 1 char"""
if len(l) != len(r): return False
for i, c1 in enumerate(l):
c2 = r[i]
if c1 == "@" or c2 == "@": continue
if c1 != c2: return False
return True
和扩展函数,其中cartesian_product
就是这样:
def expQuestions(s):
"""For OCR w/ a questionmark in them, expand questions with
@s for all possibilities"""
numqs = s.count("?")
blah = list(s)
for expqs in cartesian_product([(0,1,2,3)]*numqs):
newblah = blah[:]
qi = 0
for i,c in enumerate(newblah):
if newblah[i] == '?':
newblah[i] = '@'*expqs[qi]
qi += 1
yield "".join(newblah)
答案 2 :(得分:1)
使用Levenshtein distance可能很有用。它将给出字符串彼此相似程度的值。如果它们的长度不同,也可以使用。链接页面有一些伪造的代码可以让你入门。
你最终会得到这样的东西:
>>> match("Roflcopter", "Roflcop?er")
1
>>> match("R??lcopter", "Roflcopter")
2
>>> match("R?lcopter", "Roflcop?er")
3
所以你可以有一个最大阈值,低于你可能认为的最高阈值。
答案 3 :(得分:1)
这可能不是最Pythonic的选项,但如果允许?
匹配任意数量的字符,那么以下回溯搜索可以解决问题:
def match(a,b):
def matcher(i,j):
if i == len(a) and j == len(b):
return True
elif i < len(a) and a[i] == '?' \
or j < len(b) and b[j] == '?':
return i < len(a) and matcher(i+1,j) \
or j < len(b) and matcher(i,j+1)
elif i == len(a) or j == len(b):
return False
else:
return a[i] == b[j] and matcher(i+1,j+1)
return matcher(0,0)
这可以适应更严格的匹配。此外,为了节省堆栈空间,最终的情况(i+1,j+1
)可能会转换为非递归解决方案。
编辑:针对以下反应做出更多澄清。这是对简化正则表达式/ NFA的简单匹配算法的改编(参见Kernighan对 Beautiful Code 的贡献,O'Reilly 2007或Jurafsky&amp; Martin,语音和语言处理 ,Prentice Hall 2009)。
工作原理:matcher
函数递归遍历两个字符串/模式,从(0,0)
开始。当它到达两个字符串(len(a),len(b))
的末尾时它会成功;当遇到两个不相等的字符或一个字符串的结尾时,它会失败,而另一个字符串中仍然存在要匹配的字符。
当matcher
在任一字符串中遇到变量(?
)时(比如a
),它可以做两件事:跳过变量(匹配零个字符),或跳过在b
中的下一个字符上,但指向a
中的变量,允许它匹配更多字符。