如何在Ruby中进行模糊子字符串匹配?

eagi6jfj  于 2022-09-21  发布在  Ruby
关注(0)|答案(5)|浏览(216)

我找到了许多关于模糊匹配的链接,将一个字符串与另一个字符串进行比较,看看哪一个获得了最高的相似度分数。

我有一个非常长的字符串,它是一个文档,还有一个子字符串。子字符串来自原始文档,但已经被转换了几次,因此可能引入了奇怪的构件,例如这里的空格,那里的破折号。子字符串将与原始文档中的文本部分匹配99%或更多。我没有进行匹配以查看该字符串来自哪个文档,我正在尝试查找该字符串开始的文档中的索引。

如果字符串是相同的,因为没有引入随机错误,我将使用document.index(substring),但是,如果有一个字符差异,这个操作就会失败。

我认为可以通过删除字符串和子字符串中除a-z以外的所有字符来说明差异,比较,然后使用我在压缩字符串时生成的索引,将压缩字符串中的索引转换为实际文档中的索引。这在空格和标点符号不同的地方运行得很好,但一旦一个字母不同,它就失败了。

文档通常是几页到一百页,子字符串从几个句子到几页。

p8h8hvxi

p8h8hvxi1#

你可以试试阿玛奇。它是一颗宝石般的宝石,虽然我很久没有用过模糊逻辑了,但它看起来有你需要的东西。Ammatch的主页是:https://github.com/flori/amatch

只是无聊地胡乱处理这个想法,一个完全未经优化和未经测试的解决方案如下:

include 'amatch'

module FuzzyFinder
  def scanner( input )
    out = [] unless block_given?
    pos = 0
    input.scan(/(w+)(W*)/) do |word, white|
      startpos = pos
      pos = word.length + white.length
      if block_given?
        yield startpos, word
      else
        out << [startpos, word]
      end
    end
  end

  def find( text, doc )
    index = scanner(doc)
    sstr = text.gsub(/W/,'')
    levenshtein = Amatch::Levensthtein.new(sstr)
    minlen = sstr.length
    maxndx = index.length
    possibles = []
    minscore = minlen*2
    index.each_with_index do |x, i|
      spos = x[0]
      str = x[1]
      si = i
      while (str.length < minlen)
        i += 1
        break unless i < maxndx
        str += index[i][1]
      end
      str = str.slice(0,minlen) if (str.length > minlen)
      score = levenshtein.search(str)
      if score < minscore
        possibles = [spos]
        minscore = score
      elsif score == minscore
        possibles << spos
      end
    end
    [minscore, possibles]
  end
end

显然,有许多改进是可能的,也可能是必要的!以下是几个最重要的问题:

1.对文档进行一次处理,并将结果存储在数据库中。
1.确定初始检查的可用字符串长度,在尝试匹配整个片段之前,首先针对该初始子字符串进行处理。
1.跟进先前预先计算出的该长度的起始片段。

z31licg0

z31licg02#

简单的是fuzzy_match

require 'fuzzy_match'
FuzzyMatch.new(['seamus', 'andy', 'ben']).find('Shamus') #=> seamus

一个更详细的例子是levenshein,它计算差异的数量(但在本例中不会这么说)。

require 'levenshtein' 
Levenshtein.distance('test', 'test')    # => 0
Levenshtein.distance('test', 'tent')    # => 1
7jmck4yq

7jmck4yq3#

您应该查看下面详细介绍的StrikeAMMatch实现:A better similarity ranking algorithm for variable length strings

它不依赖于某种类型的字符串距离(即两个字符串之间的变化次数),而是查看字符对模式。每个字符串中出现的字符对越多,匹配就越好。它在我们的应用程序中工作得很好,我们在纯文本文件中搜索输入错误的/可变长度的标题。

还有一个GEM结合了StrikeAMatch(Dice's coefficient在字符级二元语法上的实现)和Levenshtein距离来查找匹配项:https://github.com/seamusabshere/fuzzy_match

e5nszbig

e5nszbig4#

这取决于可以在子字符串中结束的构件。在更简单的情况下,它们不是[a-z]的一部分,您可以使用解析子字符串,然后对文档使用Regexp#match

document = 'Ulputat non nullandigna tortor dolessi illam sectem laor acipsus.'
substr = "tortor - dolessi _%&#   +illam"

re = Regexp.new(substr.split(/[^a-z]/i).select{|e| !e.empty?}.join(".*"))
md = document.match re
puts document[md.begin(0) ... md.end(0)]

# => tortor dolessi illam

(在这里,因为我们没有在Regexp中设置任何括号,所以我们在MatchData的第一个(完全匹配)元素0上使用beginend

如果您只对起始位置感兴趣,可以使用=~运算符:

start_pos = document =~ re
mo49yndu

mo49yndu5#

我没有用过它们,但我在rubygems.org中搜索‘diff’就找到了一些库。它们都可以通过GEM进行安装。你可能想试一试。我自己也很感兴趣,所以如果你已经知道这些,或者如果你尝试过,如果你留下你的评论会很有帮助。

相关问题