使用TRE近似于python中的RegEx:奇怪的unicode行为

时间:2011-08-04 18:10:02

标签: python regex fuzzy-comparison tre-library

我正在尝试使用python中的TRE - 库来匹配拼写错误的输入 重要的是,它确实能很好地处理utf-8编码的字符串。

一个例子:
德国首都的名字是柏林,但从发音来看它是一样的,如果人们会写“Bärlin”

到目前为止,但是如果非ASCII字符位于检测到的字符串的第一个或第二个位置,则范围和检测到的字符串本身都不正确。

# -*- coding: utf-8 -*-
import tre

def apro_match(word, list):
    fz = tre.Fuzzyness(maxerr=3)
    pt = tre.compile(word)
    for i in l:
        m = pt.search(i,fz)
        if m:
            print m.groups()[0],' ', m[0]

if __name__ == '__main__':
    string1 = u'Berlín'.encode('utf-8')
    string2 = u'Bärlin'.encode('utf-8')    
    string3 = u'B\xe4rlin'.encode('utf-8')
    string4 = u'Berlän'.encode('utf-8')
    string5 = u'London, Paris, Bärlin'.encode('utf-8')
    string6 = u'äerlin'.encode('utf-8')
    string7 = u'Beälin'.encode('utf-8')

    l = ['Moskau', string1, string2, string3, string4, string5, string6, string7]

    print '\n'*2
    print "apro_match('Berlin', l)"
    print "="*20
    apro_match('Berlin', l)
    print '\n'*2

    print "apro_match('.*Berlin', l)"
    print "="*20
    apro_match('.*Berlin', l)

输出

apro_match('Berlin', l)
====================
(0, 7)   Berlín
(1, 7)   ärlin
(1, 7)   ärlin
(0, 7)   Berlän
(16, 22)   ärlin
(1, 7)   ?erlin
(0, 7)   Beälin



apro_match('.*Berlin', l)
====================
(0, 7)   Berlín
(0, 7)   Bärlin
(0, 7)   Bärlin
(0, 7)   Berlän
(0, 22)   London, Paris, Bärlin
(0, 7)   äerlin
(0, 7)   Beälin

对于正则表达式'.*Berlin'而言,它的工作正常,而对于正则表达式'Berlin'

u'Bärlin'.encode('utf-8')    
u'B\xe4rlin'.encode('utf-8')
u'äerlin'.encode('utf-8')

不起作用,而

u'Berlín'.encode('utf-8')
u'Berlän'.encode('utf-8')
u'London, Paris, Bärlin'.encode('utf-8')
u'Beälin'.encode('utf-8')

按预期工作。

我的编码错误了吗?你知道诀窍吗?

3 个答案:

答案 0 :(得分:6)

您可以使用新的regex库,它支持Unicode 6.0和模糊匹配:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from itertools import ifilter, imap
import regex as re

def apro_match(word_re, lines, fuzzy='e<=1'):
    search = re.compile(ur'('+word_re+'){'+fuzzy+'}').search
    for m in ifilter(None, imap(search, lines)):
        print m.span(), m[0]

def main():
    lst = u'Moskau Berlín Bärlin B\xe4rlin Berlän'.split()
    lst += [u'London, Paris, Bärlin']
    lst += u'äerlin Beälin'.split()
    print
    print "apro_match('Berlin', lst)"
    print "="*25
    apro_match('Berlin', lst)
    print 
    print "apro_match('.*Berlin', lst)"
    print "="*27
    apro_match('.*Berlin', lst)

if __name__ == '__main__':
    main()

'e<=1'表示最多允许任何类型的错误。有三种类型的错误:

  • 插入,由“i”
  • 表示
  • 删除,由“d”
  • 表示
  • 替换,由“s”
  • 表示

输出

apro_match('Berlin', lst)
=========================
(0, 6) Berlín
(0, 6) Bärlin
(0, 6) Bärlin
(0, 6) Berlän
(15, 21) Bärlin
(0, 6) äerlin
(0, 6) Beälin

apro_match('.*Berlin', lst)
===========================
(0, 6) Berlín
(0, 6) Bärlin
(0, 6) Bärlin
(0, 6) Berlän
(0, 21) London, Paris, Bärlin
(0, 6) äerlin
(0, 6) Beälin

答案 1 :(得分:2)

内部TRE在字节级工作,它返回字节位置。我刚才有同样的问题 - 没有诀窍!

我修改了Python绑定,添加了一个utf8函数和一个从字节位置到字符位置构建映射的函数,以及一个小包装器。使用此包装器时,您的测试用例按预期工作。我没有发布修改,在测试TRE时更多的是快速破解 - 如果你想让它们让我知道。

AFAIK TRE还没有更新很长一段时间,当前版本(0.8.0)中仍存在未修复的错误,这些错误与字符串末尾的模式匹配有关(例如搜索&#34; 2004& #34;使用模式&#34; 2004 $&#34;给出成本2,而预期成本为1)。

正如其他人所指出的那样,对于Python来说,新的正则表达式模块看起来非常有趣!

答案 2 :(得分:-1)

您提供的链接是一篇博客文章,该文章提供了另一篇关于最新版本的博客文章的引用,该文章有许多令人讨厌的评论,其中一条建议该软件包不适用于“非拉丁文”(无论如何)意思是)编码。是什么让你相信TRE使用UTF-8编码的文本(通过在字符级而不是字节级工作)?

您没有告诉我们有多少错误(插入,删除,替换)被接受为模糊匹配。 您没有告诉我们它是使用char例程还是wchar例程。你真的希望潜在的回答者能够下载这个包并阅读Python界面的代码吗?

可以预期,如果有可用的wchar C ++例程,Python接口将包含执行Python unicode的绑定&lt; - &gt; Python str(以UTF-16LE编码)&lt; - &gt; C ++ wchar - 不是吗?

鉴于6个字符的测试用例的“工作”匹配返回(0,7),而一个不工作的情况(字符串6)正在拆分一个双字节字符(打印为{{1}因为答案是无效的UTF-8),似乎它在字节(char)编码不可知模式下工作 - 根本不是一个好主意。

请注意,如果所有其他操作都失败并且您的所有输入数据都是德语,则可以尝试使用字节模式的latin1或cp1252编码。

进一步评论:

你的string3是多余的 - 它与string2相同。

你断言string5“工作”似乎与你的断言不一致,即string2和string3“工作”。

您的测试覆盖范围很少;它需要几个不匹配的情况,比“Moskau”更接近匹配!

您应该首先确保它“仅使用”ASCII数据;这里有一些测试用例:

?

然后在上面的列表中使用非ASCII字符而不是每个Berlxn Berlxyn Bxrlin Bxyrlin xerlin xyerlin Bexlin Bexylin xBerlin xyBerlin Bxerlin Bxyerlin Berlinx Berlinxy erlin Brlin Berli y`运行它。

使用像“。* Berlin”这样的模式对于诊断目的不是很有用,特别是当你没有有意义的“不应该匹配”的测试用例时。