GitHub地址:https://github.com/fanorfan/EnglishWordErrorCorrection

目录

  • 单词纠错算法
    • python实现
      • 1. 英文单词纠错(CorrectWords.py)
      • 2. word文档纠错(CorrectDocx.py)
  • 编辑距离
    • 动态规划算法
    • python实现(DynamicProgramming.py)

单词纠错算法

  1. 准备一个语料库,里面包含的内容如下:
  • Gutenberg语料库数据
  • 维基词典
  • 英国国家语料库中的最常用单词列表

下载地址:https://github.com/fanorfan/EnglishWordErrorCorrection/blob/master/big.txt

  1. 取出语料库中所有的单词,并统计其出现的次数。

  2. 对于一个给定的单词,不管其是否拼写错误,依次找到和它编辑距离为0、1、2的单词。

  3. 考虑以下三个优先顺序来挑选出该单词一个最佳纠正结果:

     1). 编辑距离为0的单词(即该单词本身) >    编辑距离为1的单词 > 编辑距离为2的单词2). 是否在语料库中出现(最起码得是一个正确的存在的单词)3). 语料库中出现的次数

python实现

1. 英文单词纠错(CorrectWords.py)
  1. 语料库的处理
import re, collections# text中所有单词转为小写形式并匹配
# text示例:"The Project Gutenberg EBook of The Adventures of Sherlock Holmes\nby Sir Arthur Conan Doyle"
def tokens(text):return re.findall("[a-z]+", text.lower())# 打开语料库文件
with open('big.txt', 'r') as f:words = tokens(f.read())
  1. 取出语料库中所有的单词,并统计其出现的次数。
# 统计语料库中每个单词的个数
word_counts = collections.Counter(words)
  1. 单词纠正
    创建一个单词纠正函数如下:
# text示例:'fianlly helloa cta'
def correct_text_generic(text):"""纠正匹配到的单词中所有拼写错误的单词"""return re.sub('[a-zA-Z]+', correct_match, text)

correct_match参数是一个回调函数,来执行对匹配到的单词的操作,我们创建这个函数如下:

def correct_match(match):"""替换的回调函数"""# 匹配到的单词依次取出,第一次取'fianlly'word = match.group()def case_of(text):"""返回小写"""return (str.upper if text.isupper() elsestr.lower if text.islower() elsestr.title if text.istitle() elsestr)return case_of(word)(correct(word.lower()))

case_of(word)()的第二个括号中需要一个函数,我们创建这个函数如下:

def correct(word):"""获得输入单词的最佳正确拼写"""# 1.是否在语料库中出现# 2.单词的优先顺序:编辑距离为0的单词(即该单词本身) > 编辑距离为1的单词 > 编辑距离为2的单词# 3.在语料库中的出现次数candidates = (known(edits0(word)) orknown(edits1(word)) orknown(edits2(word)) or{word})return max(candidates, key=word_counts.get)

candidates变量来保存符合1). 2).两个条件的单词,最后返回其中在语料库中出现次数最多的单词。

2). 是否在语料库中出现对应的函数如下:

def known(words):"""判断words中的每一个单词是否在语料库中出现,若出现就返回此单词"""return {w for w in words if w in word_counts}

1). 编辑距离为0的单词(即该单词本身) > 编辑距离为1的单词 > 编辑距离为2的单词 对应的函数如下:

def edits0(word):"""返回跟输入单词是0距离的单词,也就是自己"""return {word}def edits1(word):"""返回跟输入单词是1距离的单词"""# 26个英文字母   ord():获取'a'的码  chr():通过码还原对应的字符# 'abcdefghijklmnopqrstuvwxyz'alphabet = ''.join([chr(ord('a') + i) for i in range(26)])def splits(word):"""分割单词    以cta为例:  [('', 'cta'), ('c', 'ta'), ('ct', 'a'), ('cta', '')]"""return [(word[:i], word[i:])for i in range(len(word) + 1)]# 分割好的单词pairs = splits(word)# 删除某个字符  ['ta', 'ca', 'ct']deletes = [a + b[1:] for (a, b) in pairs if b]# 两个字符换位置   ['tca', 'cat']transposes = [a + b[1] + b[0] + b[2:] for (a, b) in pairs if len(b) > 1]# 替换某个字符 ['ata', 'bta', 'cta', 'dta', 'eta', 'fta', 'gta', 'hta', 'ita', 'jta', 'kta', 'lta', 'mta', 'nta', 'ota', 'pta', 'qta', 'rta', 'sta', 'tta', 'uta', 'vta', 'wta', 'xta', 'yta', 'zta', 'caa', 'cba', 'cca', 'cda', 'cea', 'cfa', 'cga', 'cha', 'cia', 'cja', 'cka', 'cla', 'cma', 'cna', 'coa', 'cpa', 'cqa', 'cra', 'csa', 'cta', 'cua', 'cva', 'cwa', 'cxa', 'cya', 'cza', 'cta', 'ctb', 'ctc', 'ctd', 'cte', 'ctf', 'ctg', 'cth', 'cti', 'ctj', 'ctk', 'ctl', 'ctm', 'ctn', 'cto', 'ctp', 'ctq', 'ctr', 'cts', 'ctt', 'ctu', 'ctv', 'ctw', 'ctx', 'cty', 'ctz']replaces = [a + c + b[1:] for (a, b) in pairs for c in alphabet if b]# 插入某个字符 ['acta', 'bcta', 'ccta', 'dcta', 'ecta', 'fcta', 'gcta', 'hcta', 'icta', 'jcta', 'kcta', 'lcta', 'mcta', 'ncta', 'octa', 'pcta', 'qcta', 'rcta', 'scta', 'tcta', 'ucta', 'vcta', 'wcta', 'xcta', 'ycta', 'zcta', 'cata', 'cbta', 'ccta', 'cdta', 'ceta', 'cfta', 'cgta', 'chta', 'cita', 'cjta', 'ckta', 'clta', 'cmta', 'cnta', 'cota', 'cpta', 'cqta', 'crta', 'csta', 'ctta', 'cuta', 'cvta', 'cwta', 'cxta', 'cyta', 'czta', 'ctaa', 'ctba', 'ctca', 'ctda', 'ctea', 'ctfa', 'ctga', 'ctha', 'ctia', 'ctja', 'ctka', 'ctla', 'ctma', 'ctna', 'ctoa', 'ctpa', 'ctqa', 'ctra', 'ctsa', 'ctta', 'ctua', 'ctva', 'ctwa', 'ctxa', 'ctya', 'ctza', 'ctaa', 'ctab', 'ctac', 'ctad', 'ctae', 'ctaf', 'ctag', 'ctah', 'ctai', 'ctaj', 'ctak', 'ctal', 'ctam', 'ctan', 'ctao', 'ctap', 'ctaq', 'ctar', 'ctas', 'ctat', 'ctau', 'ctav'...inserts = [a + c + b for (a, b) in pairs for c in alphabet]# 返回集合return set(deletes + transposes + replaces + inserts)def edits2(word):"""返回跟输入单词是2距离的单词寻找跟word编辑距离为1的单词的编辑距离为1的单词就是编辑距离为2的单词"""return {e2 for e1 in edits1(word) for e2 in edits1(e1)}

说明:与该单词编辑距离为2的单词,也就是与该单词的编辑距离为1的单词的编辑距离为1的单词。

最后我们验证一下效果:

if __name__ == '__main__':original_word = 'fianlly helloa cta'correct_word = correct_text_generic(original_word)print('Original word:%s\nCorrect word:%s' % (original_word, correct_word))

输出结果如下:

Original word:fianlly helloa cta
Correct word:finally hello cat

2. word文档纠错(CorrectDocx.py)

1.导入相关模块,读取doc文档

from docx import Document
from nltk import sent_tokenize, word_tokenize
from CorrectWords import correct_text_generic
from docx.shared import RGBColor# 文档中修改的单词个数
count_correct = 0
# 获取文档对象
file = Document("ErrorDocument.docx")
# 各种标点符号
punkt_list = r",.?\"'!()/\\-<>:@#$%^&*~"
document = Document()  # word文档句柄

2.创建修改每一段中错误单词的函数如下:

def write_correct_paragraph(i):"""修改一个段落中的错误"""global count_correct# 每一段的内容paragraph = file.paragraphs[i].text.strip()# 进行句子划分sentences = sent_tokenize(text=paragraph)# 词语划分words_list = [word_tokenize(sentence) for sentence in sentences]# 段落句柄p = document.add_paragraph(' ' * 7)for word_list in words_list:for word in word_list:if word not in punkt_list:p.add_run(' ')# 修改单词,如果单词正确,则返回原单词correct_word = correct_text_generic(word)# 每一句话第一个单词的第一个字母大写,并空两格if word_list.index(word) == 0 and words_list.index(word_list) == 0:correct_word = correct_word[0].upper() + correct_word[1:]# 如果单词有修改,则颜色为红色if correct_word != word:colored_word = p.add_run(correct_word)font = colored_word.fontfont.color.rgb = RGBColor(0xFF, 0x00, 0x00)count_correct += 1else:p.add_run(correct_word)else:p.add_run(word)
  • count_correct设置为全局变量,将一段话划分为句子列表,再将每一句话划分为单词列表。
  • 对每一个单词进行遍历,如果是punkt_list中的符号,直接添加;如果是单词,先加一个空格(英语句子中两个单词之间得有空格)。
  • 接着纠正单词,调用的是CorrectWords.py中的correct_text_generic()函数。
  • 如果是一句话的开头,第一个单词首字母大写。
  • 如果单词有修改,设置其颜色为红色,文档中修改的单词个数加1。

最后我们验证一下效果:

if __name__ == '__main__':print("段落数:" + str(len(file.paragraphs)))for i in range(len(file.paragraphs)):write_correct_paragraph(i)document.save("CorrectDocument.docx")print("修改并保存文件完毕!")print("一共修改了%d处。" % count_correct)

输出结果如下:

段落数:4
修改并保存文件完毕!
一共修改了19处。

ErrorDocument.docx文档

CorrectDocument.docx文档

编辑距离

什么是两个字符串的编辑距离(edit distance)?给定字符串s1和s2,以及在s1上的如下操作:

  • 删除(delete)一个字符
  • 交换(transpose)两字符位置
  • 替换(replace)一个字符
  • 插入(insert)一个字符

最少需要多少次操作才能使s1转换为s2?

比如:
单词“cat”和“hat”,这样的操作最少需要1次,只需要把“cat”中的“c”替换为“h”即可。
单词“recall”和“call”,这样的操作最少需要2次,只需要把“recall”中的“r”和“e”去掉即可。
单词“Sunday”和“Saturday”,这样的操作最少需要3次,在“Sunday”的“S”和“u”中插入“a”和“t”,再把“n”替换成“r”即可。
以上操作对应的编辑距离分别为1,2,3。
那么,是否存在一种高效的算法,能够快速、准确地计算出两个字符串的编辑距离呢?

动态规划算法

我们使用动态规划算法(Dynamic Programming)来计算出两个字符串的编辑距离。
我们从两个字符串s1和s2的最末端向前遍历,假设s1的长度为m,s2的长度为n,算法如下:
1.如果两个字符串的最后一个字符一样,那么,我们就可以递归地计算长度为m-1和n-1的两个字符串的情形;
2.如果两个字符串的最后一个字符不一样,那么,进入以下三种情形(选择其后续总共需要操作步骤最少的):

  • 删除:递归地计算长度为m-1和n的两个字符串的情形,这是在s1中的末端删除了一个字符;
  • 替换:递归地计算长度为m-1和n-1的两个字符串的情形,这是因为把s1中末端字符替换成了s2的最后一个字符,这样s1和s2的末端字符一样,就是1中情形;
  • 插入:递归地计算长度为m和n-1的两个字符串的情形,这是因为在s1中的末端插入了一个s2的最后一个字符,这样s1和s2的末端字符一样,就是1中情形;

如果m为0,则至少需要操作n次,即在s1中逐个添加s2的字符,一共是n次;如果n为0,则至少需要操作m次,即把s1的字符逐个删除即可,一共是m次。

python实现(DynamicProgramming.py)

计算编辑距离的函数如下:

# 计算编辑距离
def edit_distance(s1, s2):print("---------------------------------------")print("s1的值:", s1)print("s2的值:", s2)# s1 长度mm = len(s1)# s2 长度nn = len(s2)# 如果m = 0,则至少还需要操作n次,即在s1中逐个添加s2的字符if m == 0:if n > 0:return nelse:return 0# 如果n = 0,则至少还需要操作m次,即在s1中逐个删除字符# 如果m =0,n = 0,则不需要再操作if n == 0:if m > 0:return melse:return 0# s1和s2最后一个字符一样if s1[-1] == s2[-1]:return edit_distance(s1[:-1], s2[:-1])else:  # 最后一个字符不一样# 插入    删除      替换return 1 + min(edit_distance(s1, s2[:-1]), edit_distance(s1[:-1], s2), edit_distance(s1[:-1], s2[:-1]))
  • 如果s1长度m已经为0,s2长度n不为0,则还需要s2长度n的操作次数,即在s1中逐个添加s2的字符。同理,如果s2长度n已经为0,s1长度m不为0,则还需要s1长度m的操作次数,即把s1的字符逐个删除即可。
  • 如果s1和s2最后一个字符一样,直接递归迭代长度为m-1和n-1的两个字符串的情形
  • 如果s1和s2最后一个字符不一样,则操作数加1,且选择插入、删除、替换,三操作中以后递归迭代完毕后需要的操作总次数最少的那个操作。

最后我们验证一下效果:

if __name__ == '__main__':string1 = "Sunday"string2 = "Saturday"distance = edit_distance(string1, string2)print("编辑距离:", distance)

输出结果如下:

编辑距离: 3

参考博客

  1. 用 Python 实现英文单词纠错功能
  2. 用动态规划算法计算字符串的编辑距离

python脚本实现英文单词纠错相关推荐

  1. 网页版python叫什么-python脚本和网页有何区别

    Python是一种计算机程序设计语言,一种面向对象的动态类型语言,一种脚本语言.最初被设计用于编写自动化脚本(shell)的,常用于各种服务器的维护和自动化运行.它具有丰富和强大的库.它常被昵称为胶水 ...

  2. 网页运行python脚本_python脚本和网页有何区别

    Python是一种计算机程序设计语言,一种面向对象的动态类型语言,一种脚本语言.最初被设计用于编写自动化脚本(shell)的,常用于各种服务器的维护和自动化运行.它具有丰富和强大的库.它常被昵称为胶水 ...

  3. 命令行运行Python脚本时传入参数的三种方式

    三种常用的方式 如果在运行python脚本时需要传入一些参数,例如gpus与batch_size,可以使用如下三种方式. python script.py 0,1,2 10 python script ...

  4. Python脚本语言写法

    Python脚本语言写法 脚本语言的开始行,是指文件中的代码用什么可执行程序去运行它,就这么简单. #!/usr/bin/python是告诉操作系统执行这个脚本的时候,调用/usr/bin下的pyth ...

  5. python 脚本撞库国内“某榴”账号

    其实日常生活中我们的用户名和密码就那么几个,所以这给撞库带来了可能,本文主要给出python脚本撞库的一点粗浅代码.这里只讨论技术本生,代码中某榴的地址也已经改掉,避免被管理员误解禁言等发生,谢谢大家 ...

  6. python脚本编写_【PyQGIS】编写用于处理框架(QGIS3)的Python脚本

    可以编写可通过QGIS中的Python控制台运行的独立pyqgis脚本.进行一些调整,即可使您的独立脚本通过处理框架运行.这具有几个优点.首先,获取用户输入和写入输出文件要容易得多,因为Process ...

  7. python脚本——图片重命名、图片合成视频、faster-rcnn画P-R曲线

    调试faster rcnn算法实用的python脚本 目录 调试faster rcnn算法实用的python脚本 一.前言 二.常用python脚本 三.后记 一.前言 最近在做关于目标检测算法的研究 ...

  8. Blender中的Python脚本介绍学习教程

    Blender中的Python脚本介绍学习教程 MP4 |视频:h264,1280×720 |音频:AAC,48000 Hz 语言:英语+中英文字幕(根据原英文字幕机译更准确)|大小解压后:1.63 ...

  9. 【python】使用python脚本将LFW数据中1672组同一个人多张照片拷贝出来

    使用python脚本将LFW数据中1672组同一个人多张照片拷贝出来 dataCleaning4multiple.py 源码如下: import os, random, shutil import s ...

最新文章

  1. Python--yield关键字的使用
  2. 【Flask项目2】定制统一的JSON返回格式(6)
  3. 【CentOS7】【docker】常用操作命令
  4. 几款Java开发者必备常用的工具,准点下班不在话下
  5. 13.相机和图像——介绍,太阳摄影机,成像系统,图像形成,光圈(Aperture)_1
  6. 计算机学术英语常见词汇短语总结
  7. 【Hadoop】HDFS数据复制
  8. 征集 | AAAI 2021线下论文预讲会讲者征集
  9. matlab里的deploy,MATLAB deploytool simulink未定义函数'load_system'
  10. Java项目开发规范参考
  11. Python爬虫实战 | (13) 爬取新浪滚动新闻
  12. java 调用gephi_Gephi可视化(一)——使用Gephi Toolkit创建Gephi应用
  13. 【CXY】JAVA基础 之 语法基础
  14. AnyviewC编程作业系统——支持程序可视化运行、调试和测评
  15. java blueprint_OSGI Blueprint入门之四
  16. 别踩白块_前端H5游戏毕设
  17. C++入门第一阶段——基础篇
  18. 使用Python和C++的写数据结构和算法
  19. SpringBoot 出现 Content type ‘application/x-www-form-urlencoded;charset=UTF-8‘ not supported
  20. oracle字符串函数(转)

热门文章

  1. arduino笔记26:8*8LED矩阵
  2. PX4的CMake解析
  3. 漫画图解JWT设计单点登录系统
  4. 0x00007FF9BF948828 (Qt5Core.dll) (QtGuiApplication3.exe 中)处有未经处理的异常: 请求了严重的程序退出——解决方案
  5. Vue2的路由和异步请求
  6. VScode设置字体大小
  7. 怎么解决长期戴口罩脸过敏的肌肤问题
  8. 一只奇鸽 for app —— 集工具、功能于一身的有趣应用
  9. 从作用域到作用域链,思维脑图+代码示例让知识点一目了然!系列(三)
  10. pipenv使用教程