资讯详情

Python解析.doc练习题:从Word文档到结构化题库的完整方案

📅 2026/10/21 8:21:51 | 华诺云谱 👁 阅读
Python解析.doc练习题:从Word文档到结构化题库的完整方案
简介这份《计算机应用基础》练习题带参考答案文档面向正在备考计算机等级考试、修读计算机基础课程或需要系统梳理办公软件知识点的学生与自学者帮助其通过判断题、单项选择、多项选择等题型巩固Excel、Word、PowerPoint、Internet及HTML语言等核心内容。资源包内含1个doc文件整体约100KB轻量易存可直接打印或在线查阅适合课堂练习、考前自测与知识点查漏补缺。文档共收录40道题目并附参考答案覆盖Excel工具栏与函数输入、Windows快捷方式与画图操作、Word自动更正与替换、PowerPoint视图与文本框、Internet中TCP/IP协议与HTML代码结构等高频考点题目表述贴近真实考试风格。目前已有273人学习下载可作为计算机应用基础复习阶段的配套练习材料帮助读者快速定位薄弱环节并对照答案理解原理。1. 一份 .doc 练习题为什么值得当成一个技术项目来做《计算机应用基础》练习题带参考答案.doc 这个标题乍看像是一份普通的课程资料但在实际工作中它代表了一类非常典型的文档处理需求把一份带答案的 Word 练习题变成可检索、可批改、可批量分发的结构化数据。很多做教务系统、在线题库、培训平台的朋友都遇到过这个场景——手里拿到一份几十页的 .doc 文件里面是选择题、填空题、判断题混排答案有的在题后括号里有的统一放在文末。手工整理一百道题能对到眼花。直接扔给程序解析格式稍微一变就翻车。这个方向适合三类人一是需要把纸质或 Word 题库数字化的教务/培训从业者二是想练手文档解析的 Python 初学者因为 .doc 格式比 .docx 更“原始”能逼着你理解二进制文档结构三是需要批量生成练习卷的一线讲师把答案抽出来之后可以随机组卷、生成不带答案的版本。核心要解决的问题就一个怎么把 .doc 里的题目和答案稳定地拆出来并且保留可校验的对应关系。下面我从格式选型开始一步步拆到能跑通的代码和参数。2. 先搞清楚 .doc 和 .docx 的解析路线差异2.1 为什么不能直接用 python-docx 读 .docpython-docx 这个库名字很直白但它只认 .docx也就是 Office 2007 之后基于 XML 和 ZIP 的格式。你拿一个真正的 .doc 文件OLE 复合文档二进制结构丢给它它会直接报PackageNotFoundError因为它找不到 ZIP 的魔数PK。这是最常见的第一个坑很多人看到“Word 文档”就默认用 python-docx结果在 .doc 上卡住。那怎么判断手里的是哪种看文件头就行。用 Python 读前 8 个字节# 判断 Word 文档的真实格式 def detect_word_format(filepath): with open(filepath, rb) as f: header f.read(8) # .docx 是 ZIP 格式开头为 PK\x03\x04 if header[:4] bPK\x03\x04: return docx # .doc 是 OLE 复合文档开头为 D0 CF 11 E0 if header[:4] b\xd0\xcf\x11\xe0: return doc return unknown print(detect_word_format(练习题.doc)) # 输出 doc print(detect_word_format(练习题.docx)) # 输出 docx这段逻辑说明很简单文件头是格式的“指纹”比扩展名可靠。参数上没什么可调的读 8 个字节足够。如果你拿到的是 .doc有两条路一是用 LibreOffice 的无头模式转成 .docx 再解析二是用 antiword 或 textract 这类工具直接抽文本。我一般会先转格式因为后续处理统一在 .docx 上做工具链更成熟。2.2 转格式的两种落地方式与参数第一种命令行调用 LibreOffice。Linux 上装好 libreoffice 之后# 无头模式把 .doc 转成 .docx输出到 out 目录 libreoffice --headless --convert-to docx --outdir ./out ./练习题.doc关键参数是--headless不加会尝试弹 GUI 导致卡住--convert-to docx指定目标格式--outdir指定输出目录。转换完成后out 目录下会出现同名的 .docx。这个方式稳定但依赖本机装了 LibreOffice批量处理时建议加个超时控制。第二种纯 Python 方案用subprocess包一层方便在脚本里循环处理多个文件import subprocess import os def convert_doc_to_docx(doc_path, out_dir): os.makedirs(out_dir, exist_okTrue) cmd [ libreoffice, --headless, --convert-to, docx, --outdir, out_dir, doc_path ] # 超时 60 秒避免个别文件卡死整个批处理 result subprocess.run(cmd, capture_outputTrue, timeout60) if result.returncode ! 0: raise RuntimeError(f转换失败: {result.stderr.decode()}) # 返回生成的 docx 路径 base os.path.splitext(os.path.basename(doc_path))[0] return os.path.join(out_dir, base .docx)逻辑上就是拼命令、跑子进程、检查返回码。参数timeout60是血泪经验遇到过损坏的 .doc 让 LibreOffice 挂起没有超时整个队列就堵死了。转换完之后统一用 python-docx 读段落进入下一步。3. 从段落里拆题目和答案正则与状态机3.1 先摸清题目的排版规律拿到 .docx 之后第一件事不是写代码是打开文件看几十行。常见的《计算机应用基础》练习题排版有这么几种选择题题干以“1.”“2.”或“1、”开头选项是 A/B/C/D 各占一行或挤在一行答案有的紧跟在题干后的括号里比如“ ”有的在题末写“答案A”还有的整份答案统一放在文档最后形如“1. A 2. B 3. C”。我一般会先写个探针脚本把前 30 个非空段落打印出来人工确认模式from docx import Document doc Document(练习题.docx) count 0 for para in doc.paragraphs: text para.text.strip() if text: print(repr(text)) count 1 if count 30: break用repr是为了看清有没有不可见字符比如全角空格、制表符。这一步花五分钟能省后面两小时的调试。确认模式之后再决定用正则还是状态机。3.2 用正则提取题干、选项和行内答案假设排版是“1. 题干 ”后面跟“A. xxx”“B. xxx”这种。可以按行处理用正则匹配题号开头import re # 匹配以 数字点/顿号 开头的题干行 question_pattern re.compile(r^(\d)[\.、]\s*(.)) # 匹配选项行如 A. xxx 或 A、xxx option_pattern re.compile(r^([A-D])[\.、]\s*(.)) # 匹配行内答案如 A 或 (A) inline_answer_pattern re.compile(r[(]\s*([A-D])\s*[)]) questions [] current None for para in doc.paragraphs: text para.text.strip() if not text: continue q_match question_pattern.match(text) if q_match: # 遇到新题号先把上一题存起来 if current: questions.append(current) current { num: int(q_match.group(1)), stem: q_match.group(2), options: [], answer: None } # 检查题干里有没有行内答案 ans inline_answer_pattern.search(text) if ans: current[answer] ans.group(1) continue o_match option_pattern.match(text) if o_match and current: current[options].append({ label: o_match.group(1), text: o_match.group(2) }) continue if current: questions.append(current) print(f共提取 {len(questions)} 题)这段代码的逻辑是逐段扫描遇到题号就开一个新字典遇到选项就追加遇到行内答案就填 answer 字段。参数上question_pattern里的[\.、]覆盖了英文点和中文顿号两种写法\s*容忍题号和文字之间的空格。注意inline_answer_pattern用了全角和半角两套括号因为 Word 里经常混用。跑完之后questions就是一个结构化的列表可以直接转 JSON 或入库。3.3 答案在文末时用状态机切分答案区如果答案统一放在文档最后前面的正则只能拿到题干和选项answer 全是 None。这时候需要先定位答案区的起始位置。常见标志是“参考答案”“答案”单独成段。做法是先把所有段落读成列表找到标志段的下标然后对答案区单独解析def split_answers(paragraphs): # 找到答案区起始下标 answer_start None for i, p in enumerate(paragraphs): if re.match(r^参考答案|^答案, p.strip()): answer_start i break if answer_start is None: return paragraphs, [] body paragraphs[:answer_start] answer_block paragraphs[answer_start:] # 答案区常见格式1. A 2. B 或 1、A answers {} for line in answer_block: for m in re.finditer(r(\d)[\.、]\s*([A-D]), line): answers[int(m.group(1))] m.group(2) return body, answers逻辑说明先找标志段把文档切成正文和答案两块答案区用finditer而不是match因为一行里可能挤了多个“题号答案”。参数上[A-D]只覆盖选择题如果还有判断题把范围改成[A-D对错√×]即可。拿到 answers 字典后回填到前面提取的 questions 里按 num 对应。4. 避坑与排查五个真实翻车点4.1 现象转换后段落全挤在一起题号识别不到原因原 .doc 里用软回车ShiftEnter换行转成 .docx 后这些换行不是独立段落python-docx 的paragraphs读不到。解决改用doc.element.body.iter()遍历所有文本节点或者先把软回车替换成硬回车再转。我一般是在 LibreOffice 转换前用宏或手动替换但批量场景下更稳的是解析时同时读paragraph.text和run.text把 run 拼起来再按换行符切。4.2 现象正则匹配到了页码或页眉里的数字原因Word 的页眉页脚也会被 python-docx 读到里面的“第 1 页”会被^(\d)[\.、]误判成题号。解决只遍历doc.paragraphs不够要过滤掉页眉页脚。python-docx 里页眉页脚不在paragraphs里但如果你用了doc.element.body就会带进来。稳妥做法是加一个题号连续性校验如果新题号不是上一个题号加一就跳过。4.3 现象选项里的“A.”被当成新题号原因有些题干里本身包含“A.”比如“以下关于 A. 的说法”。解决选项正则只在当前题目上下文里生效且要求选项字母按 A、B、C、D 顺序出现。如果遇到不按顺序的记录警告人工复核不要硬塞。4.4 现象答案区里“1. A”和“1、A”混用导致漏提原因正则只写了一种分隔符。解决分隔符用字符集[\.、]把全角句点也加进去。另外答案之间可能用空格或换行分隔finditer比按行切更鲁棒。4.5 现象提取出的题目数量对不上少了十几道原因有些题号是“1”或“一、”这种非阿拉伯数字开头。解决先统计文档里所有可能的题号模式再决定正则。如果确实有中文数字题号加一个映射表把“一”到“十”转成数字或者直接放弃题号、按题干特征切分。5. 把结果用起来导出 JSON、生成无答案卷与校验5.1 导出结构化 JSON 并做完整性校验提取完只是第一步要能验证结果对不对。我一般会导出 JSON然后写个校验函数检查题号是否连续、每题是否有至少两个选项、选择题是否都有答案。import json def validate_questions(questions): errors [] nums [q[num] for q in questions] # 检查题号连续性 for i in range(1, len(nums)): if nums[i] ! nums[i-1] 1: errors.append(f题号不连续: {nums[i-1]} - {nums[i]}) for q in questions: if len(q[options]) 2: errors.append(f第 {q[num]} 题选项不足) if q[answer] is None: errors.append(f第 {q[num]} 题缺少答案) return errors with open(questions.json, w, encodingutf-8) as f: json.dump(questions, f, ensure_asciiFalse, indent2) errs validate_questions(questions) for e in errs: print(e)逻辑上就是遍历检查把问题打印出来人工修。参数ensure_asciiFalse保证中文正常显示indent2方便肉眼核对。校验通过之后这份 JSON 就可以直接喂给题库系统。5.2 生成不带答案的练习卷有了结构化数据生成无答案版本就是过滤字段的事。用 python-docx 反向写一个 .docxfrom docx import Document def export_practice(questions, output_path): doc Document() for q in questions: doc.add_paragraph(f{q[num]}. {q[stem]}) for opt in q[options]: doc.add_paragraph(f{opt[label]}. {opt[text]}) doc.add_paragraph() # 空行分隔 doc.save(output_path) export_practice(questions, 练习卷_无答案.docx)这段代码把题干和选项重新写进新文档不写 answer 字段。参数上没什么特别的注意add_paragraph()用来留空行不然题目会挤在一起。生成之后可以再转 PDF 分发。5.3 一个容易被忽略的校验技巧最后分享一个我踩过坑之后养成的习惯随机抽 5 道题把提取出的题干和选项跟原文档肉眼比对。自动化校验只能查结构查不了语义。有一次正则把“以下哪个不是”的“不是”吃掉了结构完全正常但题目意思反了。所以每次跑完我都会抽几道题看一眼。这个习惯帮我省过好几次返工。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑