5

我正在尝试在 python 中使用TRE -library 来匹配拼写错误的输入。
重要的是,它确实可以很好地处理 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')

按预期工作。

我的编码有什么问题吗?你知道什么诀窍吗?

4

3 回答 3

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
于 2011-09-24T06:38:01.213 回答
2

在内部,TRE 在字节级别工作,它返回字节位置。不久前我遇到了同样的问题-没有技巧!

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

AFAIK TRE 已经有一段时间没有更新了,当前版本 (0.8.0) 中仍然存在与字符串末尾的模式匹配相关的未修复错误(例如,使用模式“2004$”搜索“2004”给出了成本为 2,而预期成本为 1)。

正如其他人所指出的,对于 Python,新的 regex 模块似乎很有趣!

于 2012-05-05T18:20:22.603 回答
-1

您提供的链接是一篇博客文章,该文章引用了另一篇关于最新版本的博客文章,其中有许多抱怨的评论,包括建议该软件包不适用于“非拉丁”(无论这意味着什么)编码. 是什么让您相信 TRE 可以处理 UTF-8 编码的文本(通过在字符级别而不是字节级别工作)?

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

人们会期望,如果有可用的 wchar C++ 例程,Python 接口将包含执行 Python unicode <-> Python str(以 UTF-16LE 编码)<-> C++ wchar 的绑定——不是吗?

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

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

一些进一步的说明:

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

您对 string5 “有效”的断言似乎与您对 string2 和 string3 “有效”的断言不一致。

您的测试覆盖率很低;它需要几个比“Moskau”更接近匹配的不匹配案例!

您应该首先确保它“使用”纯 ASCII 数据;以下是一些测试用例:

Berlxn Berlxyn
Bxrlin Bxyrlin
xerlin xyerlin
Bexlin Bexylin
xBerlin xyBerlin
Bxerlin Bxyerlin
Berlinx Berlinxy
erlin Brlin Berli

然后使用非 ASCII 字符而不是x and上面列表中的每个 y` 运行它。

使用“.*Berlin”之类的模式对于诊断目的不是很有用,尤其是当您没有有意义的“不应该匹配”测试用例时。

于 2011-09-24T05:00:43.377 回答