【问题标题】:match words with few differences allowed匹配几乎没有差异的单词
【发布时间】:2018-07-11 08:07:58
【问题描述】:

我想知道是否有任何工具可以为 bash 终端匹配几乎同一个词。

在以下名为 list.txt 的文件中,每行包含 1 个单词:

ban
1ban
12ban
12ban3

很容易找到含有“ban”的词

grep -E "*ban*" list.txt

问题:

如何实际匹配具有 x 字母差异的单词? 使用搜索词“ban”,我希望 X=1 匹配“1ban”。

关于距离的概念,我想要最大: X 删除 或 X 替换 或 X 次插入

任何工具,但最好是可以在 bash 终端上作为命令行调用的工具。

注意:Levenshtein 距离将插入 2 个字母计为 1 个差异。这不是我想要的。

【问题讨论】:

  • 也许你想要grep "[a-z]anana" list.txt?甚至grep "[^[:space:]]anana" list.txt。可能,你的答案是already here
  • 如果 Python 发挥作用,您可以使用 PyPi 正则表达式包并结合正则表达式功能使用模糊匹配。
  • 您正在为一组未知的需求提供一些解决方案。这不是一个好主意,因为没有确切的要求,所有解决方案都被认为是好的和错误的 - 请用一组真实的要求更新问题。您是否在搜索整个单词?您要考虑哪些修改?只是没有插入/删除的替换?请回答问题。
  • 没错,我真的明白为什么很难回答这个问题。距离的概念可以用不同的方式来解释。我正在寻找整个单词,我实际上想要最大 X 差异(所以 1 个删除或 1 个替换或 1 个删除)。你能更新你的答案吗?

标签: regex linux bash terminal similarity


【解决方案1】:

您可以使用支持模糊匹配的 Python PyPi regex 类。

由于您实际上想要匹配具有最大 X 差异的单词(1 个删除或 1 个替换或 1 个删除),您可以创建一个 Python 脚本,如

#!/usr/bin/env python3
import regex, io, sys

def main(argv):
        if len(argv) < 3:
                # print("USAGE: fuzzy_search -searchword -xdiff -file")
                exit(-1)
        search=argv[0]
        xdiff=argv[1]
        file=argv[2]
        # print("Searching for {} in {} with {} differences...".format(search, file, xdiff))
        with open(file, "r") as f:
                contents = f.read()
                print(regex.findall(r"\b(?:{0}){{s<={1},i<={1},d<={1}}}\b".format(regex.escape(search), xdiff), contents))

if __name__ == "__main__":
        main(sys.argv[1:])

这里,{s&lt;=1,i&lt;=1,d&lt;=1} 表示我们允许我们搜索的单词有 1 或 0 次替换 (s&lt;=1)、1 或 0 次插入 (i&lt;=1) 或 1 或 0 次删除 (d&lt;=1)。

\b单词边界,由于这种结构,只有整个单词被匹配(vacation 中的cat 不会被匹配)。

另存为fuzzy_search.py

那么,你可以称它为

python3 fuzzy_search.py "ban" 1 file

其中"ban" 是正在执行模糊搜索的单词,1 是差异的上限。

我得到的结果是

['ban', '1ban']

您可以将输出格式更改为仅行:

print("\n".join(regex.findall(r"\b(?:{0}){{s<={1},i<={1},d<={1}}}\b".format(regex.escape(search), xdiff), contents)))

那么,结果是

ban
1ban

【讨论】:

  • 谢谢!这似乎在做这项工作......除了我不能用它来搜索 x 差异(假设你允许 2 个差异而不是 1 个)。它显然没有摆脱行尾字符。使用 f.readline().strip(),我可以在每一行上循环,但是正则表达式有问题。你能再解释一下你的表情吗?
  • @Gildas 如果您需要允许不超过 2 个替换,请将 s&lt;=1 替换为 s&lt;=2。你也可以在这里使用一个变量来通过命令行传递。你说的行尾是什么意思?你可以.strip()它。有关更多示例,请参阅the PyPi regex docs{0&lt;s&lt;4} 将允许 0、1、2 或 3 个替换。 sdi 是差异类型,您可以使用量词轻松控制。
  • @Gildas 请更新您的问题以更具体地说明您想要什么。我目前的回答为您提供了一个快速启动。
  • 你好维克托。我仍在对您的答案进行一些测试。我创建了一个变量 X 传递给正则表达式 r"(?:{}){{s
  • @Gildas 没错。如果您打算匹配整个单词,请在两端使用单词边界\br"\b(?:{0}){{s&lt;={1},i&lt;={1},d&lt;={1},e&lt;={1}}}\b".format(regex.escape(search), threshold)(其中threshold 是差异的限制)。如果您不想允许删除,请删除 ,d&lt;={1}
【解决方案2】:

您可以通过使用python检查每个字符来检查如下所示的差异,

def is_diff(str1, str2):
    diff = False
    for char1, char2 in zip(str1, str2):
        if char1 != char2:
            if diff:
                return False
            else:
                diff = True
    return diff
with open('list.txt') as f:
    data = f.readlines()

for line in data:
    print is_diff('ban', line)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-21
    • 1970-01-01
    • 1970-01-01
    • 2017-09-07
    • 2021-03-25
    • 2019-12-23
    • 2016-05-14
    • 2017-07-28
    相关资源
    最近更新 更多