python 使用 spire.doc 和 docx 对word文档进行错别字批注和修正
一、所用库
from spire.doc import *
import docx
二、按照格式获取错别字内容
需要自行获取错别字内容,可以使用大模型按照格式输出,例:
typos_info = [
{"文本错误位置": "着是一格测试百度错屋文字的说明文档", "文本错误内容": "着是一格", "文本正确内容": "这是一份", "错误原因": "'着'应为'这','一格'应为'一份',此处为形似字及语义理解错误"},
{"文本错误位置": "着是一格测试百度错屋文字的说明文档", "文本错误内容": "错屋", "文本正确内容": "错误", "错误原因": "'错屋'应为'错误',此处为形似字错误"},
{"文本错误位置": "蠢天来了,输液绿了,鸟儿在欢唱,天气变煖了,事件万物在梦雅", "文本错误内容": "蠢天", "文本正确内容": "春天", "错误原因": "'蠢天'应为'春天',此处为形似字及语义理解错误"},
{"文本错误位置": "蠢天来了,输液绿了,鸟儿在欢唱,天气变煖了,事件万物在梦雅", "文本错误内容": "煖了", "文本正确内容": "暖了", "错误原因": "'煖'应为'暖',此处为形似字错误"},
{"文本错误位置": "蠢天来了,输液绿了,鸟儿在欢唱,天气变煖了,事件万物在梦雅", "文本错误内容": "事件", "文本正确内容": "一切", "错误原因": "'事件'应为'一切',此处为形似字及语义理解错误"},
{"文本错误位置": "蠢天来了,输液绿了,鸟儿在欢唱,天气变煖了,事件万物在梦雅", "文本错误内容": "梦雅", "文本正确内容": "萌芽", "错误原因": "'梦雅'应为'萌芽',此处为形似字及语义理解错误"},
{"文本错误位置": "具体的时间详情", "文本错误内容": "时间", "文本正确内容": "事件", "错误原因": "'时间'应为'事件',此处为形似字及语义理解错误"}
]
下一步需要对这个内容进行再一步处理,进行合并。
为什么不让大模型直接输出最后需要的格式,因为代码的限制,最后需要的格式,有些许复杂,大模型理解不了,但如果你提示词用的好,当我没说,你可以忽略下面的第三步,直接让大模型输出最终需要的格式,例:
merged_typos_info = [
{'文本错误位置': '着是一格测试百度错屋文字的说明文档', '文本错误内容': ['着是一格', '错屋'], '文本正确内容': ['这是一份', '错误'], '错误原因': ["'着'应为'这','一格'应为'一份',此处为形似字及语义理解错误", "'错屋'应为'错误',此处为形似字错误"]},
{'文本错误位置': '蠢天来了,输液绿了,鸟儿在欢唱,天气变煖了,事件万物在梦雅', '文本错误内容': ['蠢天', '煖了', '事件', '梦雅'], '文本正确内容': ['春天', '暖了', '一切', '萌芽'], '错误原因': ["'蠢天'应为'春天',此处为形似字及语义理解错误", "'煖'应为'暖',此处为形似字错误", "'事件'应为'一切',此处为形似字及语义理解错误", "'梦雅'应为'萌芽',此处为形似字及语义理解错误"]},
{'文本错误位置': '具体的时间详情', '文本错误内容': ['时间'], '文本正确内容': ['事件'], '错误原因': ["'时间'应为'事件',此处为形似字及语义理解错误"]}]
三、对上面的错别字内容合并成需要的格式
def mark_typos(doc_path,typos_info,save_path):
# 创建一个新的列表用于存储去重合并后的结果
merged_typos_info = []
# 使用字典来记录每个"文本错误位置"及其对应的"文本错误内容"、"文本正确内容"和"错误原因"
location_to_data = {}
# 遍历typos_info列表
for typo in typos_info:
location = typo["文本错误位置"]
error_content = typo["文本错误内容"]
correct_content = typo["文本正确内容"]
error_reason = typo["错误原因"]
# 如果该位置已经存在于字典中,则添加错误内容、正确内容和错误原因到列表
if location in location_to_data:
location_to_data[location]["文本错误内容"].append(error_content)
location_to_data[location]["文本正确内容"].append(correct_content)
location_to_data[location]["错误原因"].append(error_reason)
else:
location_to_data[location] = {
"文本错误位置": location,
"文本错误内容": [error_content],
"文本正确内容": [correct_content],
"错误原因": [error_reason]
}
# 将字典转换为列表
for data in location_to_data.values():
merged_typos_info.append({
"文本错误位置": data["文本错误位置"],
"文本错误内容": data["文本错误内容"],
"文本正确内容": data["文本正确内容"],
"错误原因": data["错误原因"]
})
四、使用spire.doc加载文档,将错别字内容替换成占位符
因为后面需要把错别字位置替换,但是spire.doc 不能识别\n,所以要以\n进行分割,然后对每个分割出来的字符串判断有无错别字,有就替换成占位符
# 创建一个 Document 类的对象并加载一个 Word 文档
doc = Document()
doc.LoadFromFile(doc_path)
for typos in merged_typos_info:
# 因为\n匹配不了,所以进行分割,找到错误内容在第几行
if '\n' in typos['文本错误位置']:
a = typos['文本错误位置'].split('\n')
for i in a:
short_str = i
for j in range(len(typos['文本错误内容'])):
if typos['文本错误内容'][j] in i:
short_str = short_str.replace(typos['文本错误内容'][j], f'<错别字占位符{j}>')
doc.Replace(i, short_str, False, True)
else:
sign_str = typos['文本错误位置']
for j in range(len(typos['文本错误内容'])):
sign_str = sign_str.replace(typos['文本错误内容'][j], f'<错别字占位符{j}>')
doc.Replace(typos['文本错误位置'], sign_str, False, True)
五、批注和修改
批注
对所有的占位符进行批注,然后保存
for i in range(len(typos['文本错误内容'])):
text = doc.FindString(f'<错别字占位符{i}>', True, True)
# 创建一个评论并设置评论的内容和作者
comment = Comment(doc)
comment.Body.AddParagraph().Text = typos['错误原因'][i]
comment.Format.Author = "法伴"
# 将找到的文本作为文本范围,并获取其所属的段落
text_range = text.GetAsOneRange()
paragraph = text_range.OwnerParagraph
# 将评论添加到段落中
paragraph.ChildObjects.Insert(paragraph.ChildObjects.IndexOf(text_range) + 1, comment)
# 创建评论起始标记和结束标记,并将它们设置为创建的评论的起始标记和结束标记
commentStart = CommentMark(doc, CommentMarkType.CommentStart)
commentEnd = CommentMark(doc, CommentMarkType.CommentEnd)
commentStart.CommentId = comment.Format.CommentId
commentEnd.CommentId = comment.Format.CommentId
# 在找到的文本之前和之后插入创建的评论起始和结束标记
paragraph.ChildObjects.Insert(paragraph.ChildObjects.IndexOf(text_range), commentStart)
paragraph.ChildObjects.Insert(paragraph.ChildObjects.IndexOf(text_range) + 1, commentEnd)
doc.Replace(f'<错别字占位符{i}>', typos['文本错误内容'][i], False, True)
# 保存文档
doc.SaveToFile(save_path)
doc.Close()
delete_watermark(save_path)
print('批注版已完成')
修正
还是二、三部的代码,加上这个就是修正错别字的代码
for i in range(len(typos['文本错误内容'])):
doc.Replace(f"<错别字占位符{i}>", typos['文本正确内容'][i], False, True)
# 保存文档
doc.SaveToFile(save_path)
doc.Close()
delete_watermark(save_path)
print('正确版已完成')
六、解释
第一个函数是使用spire.doc读取文档,但是本程序并没有使用,可以删除
第二个函数是用来去掉spire.doc水印的(使用spire.doc保存会在第一行留下红色的一行字),使用docx读取文档,删除水印,在保存
# 使用spire.doc读取文档
def read_doc(doc_path):
# 创建一个 Document 类的对象并加载一个 Word 文档
doc = Document()
doc.LoadFromFile(doc_path)
text = doc.GetText()
text = text.replace('Evaluation Warning: The document was created with Spire.Doc for Python.\r\n','')
doc.Close()
return text
# 使用docx读取文档在保存,目的是去掉spire.doc的水印
def delete_watermark(path):
pydoc = docx.Document(path)
for para in pydoc.paragraphs:
if para.text == 'Evaluation Warning: The document was created with Spire.Doc for Python.':
p = para._element
p.getparent().remove(p)
pydoc.save(path)
七、完整代码
from spire.doc import *
import docx
def read_doc(doc_path):
# 创建一个 Document 类的对象并加载一个 Word 文档
doc = Document()
doc.LoadFromFile(doc_path)
text = doc.GetText()
text = text.replace('Evaluation Warning: The document was created with Spire.Doc for Python.\r\n','')
return text
def delete_watermark(path):
pydoc = docx.Document(path)
for para in pydoc.paragraphs:
if para.text == 'Evaluation Warning: The document was created with Spire.Doc for Python.':
p = para._element
p.getparent().remove(p)
pydoc.save(path)
def mark_typos(doc_path,typos_info,save_path):
# 创建一个新的列表用于存储去重合并后的结果
merged_typos_info = []
# 使用字典来记录每个"文本错误位置"及其对应的"文本错误内容"、"文本正确内容"和"错误原因"
location_to_data = {}
# 遍历typos_info列表
for typo in typos_info:
location = typo["文本错误位置"]
error_content = typo["文本错误内容"]
correct_content = typo["文本正确内容"]
error_reason = typo["错误原因"]
# 如果该位置已经存在于字典中,则添加错误内容、正确内容和错误原因到列表
if location in location_to_data:
location_to_data[location]["文本错误内容"].append(error_content)
location_to_data[location]["文本正确内容"].append(correct_content)
location_to_data[location]["错误原因"].append(error_reason)
else:
location_to_data[location] = {
"文本错误位置": location,
"文本错误内容": [error_content],
"文本正确内容": [correct_content],
"错误原因": [error_reason]
}
# 将字典转换为列表
for data in location_to_data.values():
merged_typos_info.append({
"文本错误位置": data["文本错误位置"],
"文本错误内容": data["文本错误内容"],
"文本正确内容": data["文本正确内容"],
"错误原因": data["错误原因"]
})
# 创建一个 Document 类的对象并加载一个 Word 文档
doc = Document()
doc.LoadFromFile(doc_path)
for typos in merged_typos_info:
# 因为\n匹配不了,所以进行分割,找到错误内容在第几行
if '\n' in typos['文本错误位置']:
a = typos['文本错误位置'].split('\n')
for i in a:
short_str = i
for j in range(len(typos['文本错误内容'])):
if typos['文本错误内容'][j] in i:
short_str = short_str.replace(typos['文本错误内容'][j], f'<错别字占位符{j}>')
doc.Replace(i, short_str, False, True)
else:
sign_str = typos['文本错误位置']
for j in range(len(typos['文本错误内容'])):
sign_str = sign_str.replace(typos['文本错误内容'][j], f'<错别字占位符{j}>')
doc.Replace(typos['文本错误位置'], sign_str, False, True)
for i in range(len(typos['文本错误内容'])):
text = doc.FindString(f'<错别字占位符{i}>', True, True)
# 创建一个评论并设置评论的内容和作者
comment = Comment(doc)
comment.Body.AddParagraph().Text = typos['错误原因'][i]
comment.Format.Author = "法伴"
# 将找到的文本作为文本范围,并获取其所属的段落
text_range = text.GetAsOneRange()
paragraph = text_range.OwnerParagraph
# 将评论添加到段落中
paragraph.ChildObjects.Insert(paragraph.ChildObjects.IndexOf(text_range) + 1, comment)
# 创建评论起始标记和结束标记,并将它们设置为创建的评论的起始标记和结束标记
commentStart = CommentMark(doc, CommentMarkType.CommentStart)
commentEnd = CommentMark(doc, CommentMarkType.CommentEnd)
commentStart.CommentId = comment.Format.CommentId
commentEnd.CommentId = comment.Format.CommentId
# 在找到的文本之前和之后插入创建的评论起始和结束标记
paragraph.ChildObjects.Insert(paragraph.ChildObjects.IndexOf(text_range), commentStart)
paragraph.ChildObjects.Insert(paragraph.ChildObjects.IndexOf(text_range) + 1, commentEnd)
doc.Replace(f'<错别字占位符{i}>', typos['文本错误内容'][i], False, True)
# 保存文档
doc.SaveToFile(save_path)
doc.Close()
delete_watermark(save_path)
print('批注版已完成')
def replace_typos(doc_path,typos_info,save_path):
# 创建一个新的列表用于存储去重合并后的结果
merged_typos_info = []
# 使用字典来记录每个"文本错误位置"及其对应的"文本错误内容"、"文本正确内容"和"错误原因"
location_to_data = {}
# 遍历typos_info列表
for typo in typos_info:
location = typo["文本错误位置"]
error_content = typo["文本错误内容"]
correct_content = typo["文本正确内容"]
error_reason = typo["错误原因"]
# 如果该位置已经存在于字典中,则添加错误内容、正确内容和错误原因到列表
if location in location_to_data:
location_to_data[location]["文本错误内容"].append(error_content)
location_to_data[location]["文本正确内容"].append(correct_content)
location_to_data[location]["错误原因"].append(error_reason)
else:
location_to_data[location] = {
"文本错误位置": location,
"文本错误内容": [error_content],
"文本正确内容": [correct_content],
"错误原因": [error_reason]
}
# 将字典转换为列表
for data in location_to_data.values():
merged_typos_info.append({
"文本错误位置": data["文本错误位置"],
"文本错误内容": data["文本错误内容"],
"文本正确内容": data["文本正确内容"],
"错误原因": data["错误原因"]
})
# 创建一个 Document 类的对象并加载一个 Word 文档
doc = Document()
doc.LoadFromFile(doc_path)
for typos in merged_typos_info:
# 因为\n匹配不了,所以进行分割,找到错误内容在第几行
if '\n' in typos['文本错误位置']:
a = typos['文本错误位置'].split('\n')
for i in a:
short_str = i
for j in range(len(typos['文本错误内容'])):
if typos['文本错误内容'][j] in i:
short_str = short_str.replace(typos['文本错误内容'][j], f'<错别字占位符{j}>')
doc.Replace(i, short_str, False, True)
else:
sign_str = typos['文本错误位置']
for j in range(len(typos['文本错误内容'])):
sign_str = sign_str.replace(typos['文本错误内容'][j], f'<错别字占位符{j}>')
doc.Replace(typos['文本错误位置'], sign_str, False, True)
for i in range(len(typos['文本错误内容'])):
doc.Replace(f"<错别字占位符{i}>", typos['文本正确内容'][i], False, True)
# 保存文档
doc.SaveToFile(save_path)
doc.Close()
delete_watermark(save_path)
print('正确版已完成')
if __name__ == "__main__":
# typos_info = [
# {"文本错误位置": "树木从声", "文本错误内容": "从声", "文本正确内容": "丛生", "错误原因": "树木从声 的从声应该 丛生"},
# {"文本错误位置": "百草丰冒。\n秋封萧瑟,红波涌起。", "文本错误内容": "冒", "文本正确内容": "茂", "错误原因": "百草丰冒 的冒应该是茂盛的 茂"},
# {"文本错误位置": "百草丰冒。\n秋封萧瑟,红波涌起。", "文本错误内容": "封", "文本正确内容": "风", "错误原因": "秋封萧瑟 的封应该是刮风的 风"},
# {"文本错误位置": "百草丰冒。\n秋封萧瑟,红波涌起。", "文本错误内容": "红", "文本正确内容": "洪", "错误原因": "红波涌起 的红应该是洪水的 洪"},
# {"文本错误位置": "割以咏志", "文本错误内容": "割", "文本正确内容": "歌", "错误原因": "割以咏志 的割应该是歌唱的 歌"},
# ]
typos_info = [
{"文本错误位置": "着是一格测试百度错屋文字的说明文档", "文本错误内容": "着是一格", "文本正确内容": "这是一份", "错误原因": "'着'应为'这','一格'应为'一份',此处为形似字及语义理解错误"},
{"文本错误位置": "着是一格测试百度错屋文字的说明文档", "文本错误内容": "错屋", "文本正确内容": "错误", "错误原因": "'错屋'应为'错误',此处为形似字错误"},
{"文本错误位置": "蠢天来了,输液绿了,鸟儿在欢唱,天气变煖了,事件万物在梦雅", "文本错误内容": "蠢天", "文本正确内容": "春天", "错误原因": "'蠢天'应为'春天',此处为形似字及语义理解错误"},
{"文本错误位置": "蠢天来了,输液绿了,鸟儿在欢唱,天气变煖了,事件万物在梦雅", "文本错误内容": "煖了", "文本正确内容": "暖了", "错误原因": "'煖'应为'暖',此处为形似字错误"},
{"文本错误位置": "蠢天来了,输液绿了,鸟儿在欢唱,天气变煖了,事件万物在梦雅", "文本错误内容": "事件", "文本正确内容": "一切", "错误原因": "'事件'应为'一切',此处为形似字及语义理解错误"},
{"文本错误位置": "蠢天来了,输液绿了,鸟儿在欢唱,天气变煖了,事件万物在梦雅", "文本错误内容": "梦雅", "文本正确内容": "萌芽", "错误原因": "'梦雅'应为'萌芽',此处为形似字及语义理解错误"},
{"文本错误位置": "具体的时间详情", "文本错误内容": "时间", "文本正确内容": "事件", "错误原因": "'时间'应为'事件',此处为形似字及语义理解错误"}
]
# 读取用户上传的docx
doc_path = 'typos_doc/百度测试错别字.docx'
# 导出的批注版和正确版文件
mark_doc_path = 'result/批注.docx'
right_doc_path = 'result/正确.docx'
# 进行批注,导出批注版
mark_typos(doc_path, typos_info, mark_doc_path)
# 替换错别字,导出正确版
replace_typos(doc_path, typos_info, right_doc_path)
评论区