导语:

模糊匹配可以算是现代编辑器(在选择要打开的文件时)的一个必备特性了,它所做的就是根据用户输入的部分内容,猜测用户想要的文件名,并提供一个推荐列表供用户选择。

样例如下:

Vim (Ctrl-P)

Sublime Text (Cmd-P)

‘模糊匹配’这是一个极为有用的特性,同时也非常易于实现。

问题分析:

我们有一堆字符串(文件名)集合,我们根据用户的输入不断进行过滤,用户的输入可能是字符串的一部分。我们就以下面的集合为例:

>>> collection = ['django_migrations.py',

'django_admin_log.py',

'main_generator.py',

'migrations.py',

'api_user.doc',

'user_group.doc',

'accounts.txt',

]

当用户输入’djm‘字符串时,我们假定是匹配到’django_migrations.py’和’django_admin_log.py’,而最简单的实现方法就是使用正则表达式。

解决方案:

1.常规的正则匹配

将’djm’转换成’d.*j.*m’然后用这个正则尝试匹配集合中的每一个字符串,如果匹配到了就被列为候选。

>>> import re

>>> def fuzzyfinder(user_input, collection):

suggestions = []

pattern = '.*'.join(user_input) # Converts 'djm' to 'd.*j.*m'

regex = re.compile(pattern)     # Compiles a regex.

for item in collection:

match = regex.search(item)  # Checks if the current item matches the regex.

if match:

suggestions.append(item)

return suggestions

>>> print fuzzyfinder('djm', collection)

['django_migrations.py', 'django_admin_log.py']

>>> print fuzzyfinder('mig', collection)

['django_migrations.py', 'django_admin_log.py', 'main_generator.py', 'migrations.py']

这里根据用户的输入我们得到了一个推荐列表,但是推荐列表中的字符串是没有进行重要性区分的。有可能出现最合适的匹配项被放到了最后的情况。

实际上,还是这个例子,当用户输入’mig‘时,最佳选项’migrations.py’就被放到了最后。

2.带有rank排序的匹配列表

这里我们对匹配到的结果按照匹配内容第一次出现的起始位置来进行排序。

'main_generator.py'     - 0

'migrations.py'         - 0

'django_migrations.py'  - 7

'django_admin_log.py'   - 9

下面是相关代码:

>>> import re

>>> def fuzzyfinder(user_input, collection):

suggestions = []

pattern = '.*'.join(user_input) # Converts 'djm' to 'd.*j.*m'

regex = re.compile(pattern)     # Compiles a regex.

for item in collection:

match = regex.search(item)  # Checks if the current item matches the regex.

if match:

suggestions.append((match.start(), item))

return [x for _, x in sorted(suggestions)]

>>> print fuzzyfinder('mig', collection)

['main_generator.py', 'migrations.py', 'django_migrations.py', 'django_admin_log.py']

这次我们生成了一个由二元tuple组成的列表,即列表中的每一个元素为一个二元tuple,而该二元tuple的第一个值为匹配到的起始位置、第二个值为对应的文件名,然后使用列表推导式按照匹配到的位置进行排序并返回文件名列表。

现在我们已经很接近最终的结果了,但还称不上完美——用户想要的是’migration.py’,但我们却把’main_generator.py’作为第一推荐。

3.根据匹配的紧凑程度进行排序

当用户开始输入一个字符串时,他们倾向于输入连续的字符以进行精确匹配。比如当用户输入’mig‘他们更倾向于找的是’migrations.py’或’django_migrations.py’,而不是’main_generator.py’,所以这里我们所做的改变就是查找匹配到的最紧凑的项目。

刚才提到的问题对于Python来说不算什么事,因为当我们使用正则表达式进行字符串匹配时,匹配到的字符串就已经被存放在了match.group()中了。下面假设输入为’mig’,对最初定义的’collection’的匹配结果如下:

regex = '(m.*i.*g)'

'main_generator.py'    ->  'main_g'

'migrations.py'        ->  'mig'

'django_migrations.py' ->  'mig'

'django_admin_log.py'  ->  'min_log'

这里我们将推荐列表做成了三元tuple的列表的形式,即推荐列表中的每一个元素为一个三元tuple,而该三元tuple的第一个值为匹配到的内容的长度、第二个值为匹配到的起始位置、第三个值为对应的文件名,然后按照匹配长度和起始位置进行排序并返回。

>>> import re

>>> def fuzzyfinder(user_input, collection):

suggestions = []

pattern = '.*'.join(user_input) # Converts 'djm' to 'd.*j.*m'

regex = re.compile(pattern)     # Compiles a regex.

for item in collection:

match = regex.search(item)  # Checks if the current item matches the regex.

if match:

suggestions.append((len(match.group()), match.start(), item))

return [x for _, _, x in sorted(suggestions)]

>>> print fuzzyfinder('mig', collection)

['migrations.py', 'django_migrations.py', 'main_generator.py', 'django_admin_log.py']

针对我们的输入,这时候的匹配结果已经趋向于完美了,不过还没完。

4.非贪婪匹配

由 Daniel Rocco 发现了这一微妙的问题:当集合中有[‘api_user’, ‘user_group’]这两个元素存在,用户输入’user‘时,预期的匹配结果(相对顺序)应该为[‘user_group’, ‘api_user‘],但实际上的结果为:

>>> print fuzzyfinder('user', collection)

['api_user.doc', 'user_group.doc']

上面的测试结果中:’api_user’要排在’user_group’前面。深入一点,我们发现这是因为在搜索’user’时,正则被扩展成了’u.*s.*e.*r’,考虑到’user_group’有2个’r’,因此该模式匹配到了’user_gr‘而不是我们预期的’user‘。更长的匹配导致在最后的匹配rank排序时名次下降这一违反直觉的结果,不过这问题也容易解决,将正则修改为’非贪婪匹配’即可。

>>> import re

>>> def fuzzyfinder(user_input, collection):

suggestions = []

pattern = '.*?'.join(user_input)    # Converts 'djm' to 'd.*?j.*?m'

regex = re.compile(pattern)         # Compiles a regex.

for item in collection:

match = regex.search(item)      # Checks if the current item matches the regex.

if match:

suggestions.append((len(match.group()), match.start(), item))

return [x for _, _, x in sorted(suggestions)]

>>> fuzzyfinder('user', collection)

['user_group.doc', 'api_user.doc']

>>> print fuzzyfinder('mig', collection)

['migrations.py', 'django_migrations.py', 'main_generator.py', 'django_admin_log.py']

现在,fuzzyfinder已经可以(在上面的情况中)正常工作了,而我们不过只写了10行代码就实现了一个 fuzzy finder。

结论:

以上就是我在我的 pgcli 项目(一个有自动补全功能的Postgresql命令行实现)中设计实现’fuzzy matching’的过程记录。

我已经将 fuzzyfinder 提取成一个独立的Python包,你可以使用命令’pip install fuzzyfinder’在你的项目中进行安装和使用。

感谢 Micah Zoltu 和 Daniel Rocco 对算法的检查和问题修复。

如果你对这个感兴趣的话,你可以来 twitter (https://twitter.com/amjithr)上找我。

结语:

当我第一次考虑用Python实现“fuzzy matching”的时候,我就知道一个叫做 fuzzywuzzy 的优秀库,但是 fuzzywuzzy 的做法和这里的不太一样,它使用的是 “levenshtein distance” 来从集合中找到最匹配的字符串。”levenshtein distance“是一个非常适合用来做自动更正拼写错误的技术,但在从部分子串匹配长文件名时表现的不太好(所以这里没有使用)。

转载于:https://www.cnblogs.com/yangxiaolan/p/5782592.html

10 行 Python 代码写的模糊查询相关推荐

  1. python自动化--如何用10行Python代码调一个闹钟 #电脑调闹钟

    python自动化--如何用10行Python代码写一个闹钟 前言: 文末彩蛋--轻松一刻 更多关于Python的知识请加关注哟~~.若需联系博主请私信或者加博主联系方式:       QQ:1542 ...

  2. 10 行 Python 代码能实现哪些有趣功能?

    大家好,我是菜鸟哥 Python凭借其简洁的代码,赢得了许多开发者的喜爱.因此也就促使了更多开发者用Python开发新的模块,从而形成良性循环,Python可以凭借更加简短的代码实现许多有趣的操作.下 ...

  3. 10行 python 代码做出哪些酷炫的事情?

    来源 | Python小二 Python凭借其简洁的代码,赢得了许多开发者的喜爱.因此也就促使了更多开发者用Python开发新的模块,从而形成良性循环,Python可以凭借更加简短的代码实现许多有趣的 ...

  4. python text insert()背景色_50行python代码写个计算器教程

    案例展示 计算器.gif 你能学到 input 用户输入 print输出 tkinter图形界面 python运算符号 基础知识准备 运算符号 数字运算,求和我们使用了加号 (+)运算符,除此外,还有 ...

  5. 10行python代码可以做哪些酷炫的事情

    前言 今天玩啥?10行代码够玩吗? Python凭借其简洁的代码,赢得了许多开发者的喜爱.因此也就促使了更多开发者用Python开发新的模块,从而形成良性循环, Python可以凭借更加简短的代码实现 ...

  6. 10行python代码做出哪些酷炫的事情?

    大家好,我是辰哥~ Python凭借其简洁的代码,赢得了许多开发者的喜爱.因此也就促使了更多开发者用Python开发新的模块,从而形成良性循环,Python可以凭借更加简短的代码实现许多有趣的操作.下 ...

  7. 10行python代码做出哪些酷炫的事情? 小白教程

    Python凭借其简洁的代码,赢得了许多开发者的喜爱.因此也就促使了更多开发者用Python开发新的模块,从而形成良性循环,Python可以凭借更加简短的代码实现许多有趣的操作.下面我们来看看,我们用 ...

  8. 获取clientheight为0_用10行python代码获取全国城市交通生活圈

    今天在网上冲浪看到一个很花哨的数据,全国各城市主要商圈从早上6点到晚上10点的20min,30min,45min,60min,90min生活圈轮廓. 1 获取思路 按下F12 按下F12,点击淮海路可 ...

  9. 10行python代码的词云

    关于词云wordcloud 什么是词云呢?词云又叫文字云,是对文本数据中出现频率较高的"关键词"在视觉上的突出呈现,形成关键词的渲染形成类似云一样的彩色图片,从而一眼就可以领略文本 ...

最新文章

  1. 关于Ubuntu运行级别、开机启动脚本的说明
  2. 2.2 理解 Mini-batch 梯度下降-深度学习第二课《改善深层神经网络》-Stanford吴恩达教授
  3. 与众不同 windows phone (22) - Device(设备)之摄像头(硬件快门, 自动对焦, 实时修改捕获视频)...
  4. intellij idea14.1 卡顿问题解决
  5. Navicat Premium 11 12 闪退
  6. 程序员面试题 php_程序员面试必备PHP基础面试题 – 第十五天
  7. python控制gpio产生固定数量的脉冲_STM32L151用dma控制GPIO口发出指定的脉冲个数的疑惑!...
  8. 【转】NPOI自定义单元格背景颜色
  9. 深度学习面试题:计算机视觉训练技巧
  10. 微软快捷键截图_如何在Microsoft Office的屏幕提示中显示快捷键
  11. 提高淘宝店铺动态评分的四大技巧
  12. MATLAB伽罗域是什么,利用伽罗华域的运算来计算生成矩阵
  13. 苹果手机小圆点怎么设置?悬浮球设置,轻松学会
  14. 墨菲安全受邀与腾讯安全共话软件供应链安全治理
  15. 【MATLAB】极坐标绘图 ( Polar Chart 极坐标图 | MATLAB 绘图问题 1 )
  16. Cannot open output file: Permission denied 的处理方式
  17. python列表是顺序表还是链表_Python 中的 List 是封装了顺序存储结构还是链表存储结构?...
  18. Linux学习笔记——ubuntu必会背景知识及系统目录结构详解
  19. 计算分页页数或索引在第几页
  20. 项目范围管理和时间管理

热门文章

  1. 《Spring1之第五次站立会议》
  2. 通过共享内存进行进程间通信
  3. win8下Python学习——搭建web.py框架
  4. Windows内核情景分析 笔记
  5. PetShop 4.0讨论专贴(QA)
  6. 英雄难过棍子关html游戏开发,《英雄难过棍子关》评测:看我变长再变长!
  7. 滤波器的主要特性指标
  8. 调整数组顺序使奇数位于偶数前面【不保持相对位置】
  9. AI芯片模型设计AI芯片到底会带来怎样的未来
  10. 有哪些老鸟程序员知道而新手不知道的小技巧?自我感受