简介本资源为计算机专业英语课程期末考试试卷A的PDF版面向高校计算机相关专业学生及需要巩固专业英语词汇的备考者可用于期末复习自测与知识点查漏。试卷共三部分第一部分要求将bandwidth、browser、Control Panel、data structure、fiber-optic table、hypermedia、high-level language等十个常用术语译成中文第二部分以连线形式匹配HTTP、ISP、SQL、WWW、DBMS、BIOS、API、DVD、DMA、CPU与其对应汉语释义第三部分依据Passage A关于home page定义、设置与应用的短文判断五个陈述的正误。压缩包仅含1个PDF文件约24KB轻量易存打印或平板批注均可。目前已有331人学习浏览适合作为考前词汇梳理与概念应用训练的对照材料。1. 一份计算机专业英语期末试题及答案A.pdf 到底该怎么用期末季一过老师手里、助教群里、打印店桌面上就会躺着几份命名极其随意的文件计算机专业英语期末试题及答案A.pdf、B卷、答案页、评分标准。多数人的处理方式是打开、CtrlF、手动抄成 Word第二年换个专业方向又重新来一遍。真正值得做的是把这份 PDF 当成数据源抽成结构化题库让「查一道术语题、按知识点抽一套卷、验证答案有没有错位」变成一条命令能解决的事。下面这条链路覆盖 PDF 解析、题库建模、全文检索、反向组卷和校对验证五个环节适合计算机专业英语的任课老师、课程助教以及做教育类产品的后端与数据处理同学代码都可以直接跑。2. 解析计算机专业英语试卷 PDF先分清文本层还是扫描件同一个标题的 PDF内部结构可能完全不同。有的是 Word 直接导出的文本层 PDF每个字符都能选中复制有的是打印后拍照再合成的扫描件pdftotext抽出来一片空白。这两条路线的处理成本差一个数量级所以第一步永远是判型而不是上来就写正则。2.1 用 pdffonts 和 pdftotext 三步判型不装任何 Python 包也能判。poppler-utils里带的三个命令足够# 1) 看有没有嵌入字体有字体基本说明是文本层 PDF pdffonts 计算机专业英语期末试题及答案A.pdf # 2) 抽取纯文本看前 40 行有没有题干文字 pdftotext -layout 计算机专业英语期末试题及答案A.pdf - | head -40 # 3) 统计每页可抽取的字符数扫描件这一列会接近 0 pdftotext -layout 计算机专业英语期末试题及答案A.pdf - | wc -cpdffonts输出里如果type全是CID TrueType之类且emb列为yes说明排版信息完整走正则解析路线如果输出为空或者只有一两个Type3字体pdftotext又抽不出文字那就是纯图得走 OCR。判型结果典型特征推荐路线单页成本文本层 PDFpdffonts 有字体pdftotext 出字pdfplumber 直接抽行毫秒级半文本层题干可抽图表公式是图pdfplumber 局部 OCR秒级纯扫描件pdftotext 输出为空300dpi 渲染 OCR数秒每页2.2 pdfplumber 按行抽取题干与选项的最小脚本计算机专业英语的试卷结构通常很规整I. Vocabulary、II. Multiple Choice、III. Translate题号是1.或1、选项是A.B.C.D.答案页统一在文件末尾形如1-5 ABCDA。按行切分再分类比按坐标切分稳得多。import re import pdfplumber # 行级分类正则题干行 / 选项行 / 答案行 / 大题标题 RE_SECTION re.compile(r^\s*(I{1,3}|IV|V|VI)[\.、]\s*\S) RE_QNO re.compile(r^\s*(\d{1,3})\s*[\.、]\s*(.)) RE_OPTION re.compile(r^\s*([A-D])\s*[\.、]\s*(.)) RE_ANSWER re.compile(r^\s*(\d{1,3})\s*[-–~]\s*(\d{1,3})\s*[:]?\s*([A-D]{2,})) def parse_pdf(path): rows, section [], None with pdfplumber.open(path) as pdf: for page in pdf.pages: # layoutFalse 保留阅读顺序用 x_tolerance 抑制字符间多余空格 text page.extract_text(x_tolerance1.5) or for line in text.split(\n): line line.strip() if not line: continue if RE_SECTION.match(line): section line continue m RE_ANSWER.match(line) if m: # 答案页把 1-5 ABCDA 展开成 {1:A,2:B,...} start, end, chars int(m.group(1)), int(m.group(2)), m.group(3) for i, ch in enumerate(chars): rows.append({kind: answer, q_no: start i, value: ch}) continue m RE_QNO.match(line) if m: rows.append({kind: stem, q_no: int(m.group(1)), section: section, text: m.group(2)}) continue m RE_OPTION.match(line) if m: rows.append({kind: option, label: m.group(1), text: m.group(2)}) return rows这段代码做三件事按行分类、把大题标题作为上下文带到后续题目上、把区间形式的答案展开成逐题映射。x_tolerance是这里最关键的参数默认值 3 在英文单词间会插入多余空格抽出来的hash table直接让术语匹配失败调到 1.0 到 2.0 之间比较合适具体值拿一页实测一次即可。2.3 题号、选项、答案的对齐规则抽取出来的rows还是扁平列表需要按「题号递增」把选项归到最近的题干下。规则简单但必须显式写死遇到stem就开一个新题对象遇到option就追加到当前题对象遇到answer单独进字典最后统一合并。字段正则说明大题标题^\s*(I{1,3}IV题干行^\s*(\d{1,3})\s*[\.、]\s*(.)同时兼容「1.」和「1、」两种中文排版习惯选项行^\s*([A-D])\s*[\.、]\s*(.)只认 A–DE 选项在专业英语卷里极少区间答案^\s*(\d)\s*[-–~]\s*(\d)\s*[:]?\s*([A-D])连字符有-、–、~三种都要覆盖注意中文全角句点「。」和英文半角句点「.」在题号后混用非常常见正则里必须同时收否则会漏掉整道题。2.4 扫描件的兜底路线判型结果是纯图时先用 PyMuPDF 把每页渲染成 300dpi 的 PNG再做 OCR。命令行可以先用pdftoppm -r 300 -png批量出图识别引擎选带中英混合模型的识别语言参数写成chi_simeng版面分析用--psm 6假设为统一的文本块比默认的自动分栏更适合试卷这种单栏排版。OCR 路线最大的坑不是识别率而是字形相近混淆英文术语里的l和1、O和0、rn和m在低分辨率下几乎必错。抽完之后一定要过一遍术语表校正把cornpiler、hash tab1e这类结果映射回compiler、hash table否则后面建索引时术语查询会大面积落空。3. 题库建模与全文检索把计算机专业英语试题装进 SQLite抽出来的题目如果只是丢进一个 CSV第二年想按知识点筛题还是得手工翻。建表的意义在于把「试卷—题目—选项—术语」四层关系固定下来之后无论是查一道题还是抽一套卷都变成一条 SQL。3.1 四张表撑起一个小题库-- 试卷一份 PDF 对应一行保留来源文件名便于溯源 CREATE TABLE IF NOT EXISTS paper ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, -- 计算机专业英语期末试题及答案A term TEXT, -- 2024-2025-1 source_file TEXT ); -- 题目q_no 保留原始题号字符串避免合并卷时撞号 CREATE TABLE IF NOT EXISTS question ( id INTEGER PRIMARY KEY, paper_id INTEGER NOT NULL REFERENCES paper(id), q_no TEXT NOT NULL, q_type TEXT NOT NULL, -- choice / blank / term / reading stem TEXT NOT NULL, answer TEXT, score REAL DEFAULT 2.0, tag TEXT -- 操作系统 / 网络 / 数据库 / 编译原理 ); -- 选项只有选择题有用 seq 保证展示顺序 CREATE TABLE IF NOT EXISTS choice ( question_id INTEGER NOT NULL REFERENCES question(id), seq INTEGER NOT NULL, label TEXT NOT NULL, content TEXT NOT NULL, PRIMARY KEY (question_id, seq) ); -- 术语表计算机专业英语特有的中英对照用来做 OCR 校正和同义扩展 CREATE TABLE IF NOT EXISTS term ( id INTEGER PRIMARY KEY, en TEXT NOT NULL, zh TEXT, chapter TEXT, UNIQUE (en) ); CREATE INDEX IF NOT EXISTS idx_q_paper ON question(paper_id, q_no); CREATE INDEX IF NOT EXISTS idx_q_tag ON question(tag);q_no用 TEXT 而不是 INTEGER 是有意的原始卷子上存在「三、2」这种带大题前缀的题号合并多份试卷时如果统一转成整数A 卷的第 5 题和 B 卷的第 5 题会互相覆盖。3.2 用 FTS5 给题干和术语建索引SQLite 自带的 FTS5 虚拟表足够撑住几千道题的检索需求不需要额外起服务。-- 外部内容表模式正文仍存在 question 里FTS 只存倒排索引 CREATE VIRTUAL TABLE IF NOT EXISTS question_fts USING fts5( stem, answer, contentquestion, content_rowidid, tokenizetrigram ); -- 首次全量灌入 INSERT INTO question_fts(rowid, stem, answer) SELECT id, stem, IFNULL(answer, ) FROM question;tokenize的选择直接决定检索体验。unicode61按空格和标点切词英文题没问题中文题干会整句变成一个 token等于搜不到porter做英文词干还原compile和compiling能互相命中但对中文无效trigram按三字符滑窗建索引中英混排一视同仁「进程调度」这种中文短语和hash table这种英文短语都能子串命中代价是索引体积大约翻两到三倍。trigram 是较新版本才带的分词器先执行SELECT sqlite_version();确认老版本环境退回到unicode61但要对中文题干额外存一列用空格切好的分词结果。3.3 中英混合查询的三个参数坑参数 / 写法推荐值不这样设的后果tokenizetrigram中文题干整句不可检索content_rowidid与question.id对不上JOIN 结果错行bm25权重stem2.0、answer1.0答案列命中排在题干命中前面结果不直观查询词引号短语加双引号deadlock长短语被拆词后召回大量无关题contentquestion是「外部内容表」模式指的是 FTS 表本身不存原文只存索引查询时还要 JOIN 回question取正文。这样省空间但代价是主表数据变更后必须手工同步索引-- 题目被修改后同步索引delete 老行 insert 新行 INSERT INTO question_fts(question_fts, rowid, stem, answer) VALUES(delete, :id, :old_stem, :old_answer); INSERT INTO question_fts(rowid, stem, answer) VALUES(:id, :new_stem, :new_answer);3.4 三条例行检索查术语题、查知识点、查答案命中-- 场景一找所有含 hash table 的选择题按相关度排序并高亮 SELECT q.id, q.q_no, q.tag, snippet(question_fts, 0, [, ], …, 14) AS hit FROM question_fts f JOIN question q ON q.id f.rowid WHERE question_fts MATCH ? AND q.q_type choice ORDER BY bm25(question_fts, 2.0, 1.0) LIMIT 20; -- 场景二按知识点配额统计为组卷做准备 SELECT tag, COUNT(*) AS n FROM question GROUP BY tag ORDER BY n DESC; -- 场景三反向查答案找出答案列里含 deadlock 的题 SELECT id, q_no, stem FROM question WHERE id IN (SELECT rowid FROM question_fts WHERE question_fts MATCH answer:deadlock);第三条用的是列过滤语法列名:关键词需要 FTS5 表建了多列索引才有效。参数?里传的查询串建议在应用层做一次清洗把用户输入的转义成否则一个引号就能让 MATCH 抛语法错误。4. 从题库反向组卷生成新的计算机专业英语期末试题及答案A.pdf题库建好之后真正的效率提升点是反向组卷——不再从零出题而是从现有题库里按知识点配额抽题、重新排版、一次导出试卷和答案页。这比手工复制粘贴平台化得多也更容易保证 A/B 卷难度对齐。4.1 按知识点配额抽题的一条 SQL不要简单ORDER BY RANDOM() LIMIT 20那样很可能抽出一堆网络题、一道操作系统题都没有。用窗口函数按 tag 分区随机排序再取每个分区的前 N 条WITH ranked AS ( SELECT id, tag, ROW_NUMBER() OVER (PARTITION BY tag ORDER BY RANDOM()) AS rn FROM question WHERE q_type :q_type -- choice / term / blank AND score :min_score ) SELECT q.* FROM question q JOIN ranked r ON r.id q.id WHERE r.rn :quota_per_tag; -- 每个知识点最多抽几道PARTITION BY tag决定配额的粒度想按「章节 题型」双重配额就写成PARTITION BY tag, q_type。ORDER BY RANDOM()在几千行规模下完全够用题库上到十万行之后建议改成先随机取一个 id 起点再顺序捞避免全表排序。4.2 Jinja2 WeasyPrint 渲染试卷与答案页排版用 HTML 模板写导出 PDF 交给 WeasyPrint比直接调 ReportLab 画坐标省事得多中文字体也只要在 CSS 里声明一次。from jinja2 import Template from weasyprint import HTML TPL Template( style page { size: A4; margin: 2cm 1.8cm; } body { font-family: Noto Serif CJK SC, serif; font-size: 11pt; line-height: 1.7; } .q { margin: 0 0 10pt 0; page-break-inside: avoid; } .opt { margin-left: 1.5em; } /style h2{{ title }}/h2 {% for q in questions %} div classq div{{ loop.index }}. {{ q.stem }}/div {% for o in q.options %} div classopt{{ o.label }}. {{ o.content }}/div {% endfor %} /div {% endfor %} ) def render(paper, questions, out_pdf): html TPL.render(titlepaper[name], questionsquestions) HTML(stringhtml, base_url.).write_pdf(out_pdf)page-break-inside: avoid是试卷排版里最值得加的一条 CSS不加的话一道四选项的选择题会被撕成两页考生翻页找选项。loop.index用来重新编号抽题顺序变了题号自动跟着走不用手工维护。4.3 A/B 卷题序打乱与答案映射答案页错位是自动组卷最常见的翻车点试卷上题号重排了答案还是按题库 id 顺序输出。解决办法是在抽取阶段就生成一份显式的映射表让试卷和答案页共用同一个索引。字段含义组卷时必须做什么display_no卷面上显示的题号重排后重新生成从 1 连续递增question_id题库主键全程只读用于回溯原始题answer_key正确答案单独渲染成答案页禁止与题干同页seed本次抽题的随机种子必须落库否则 B 卷无法用同分布重放import random def build_paper(questions, shuffle_optionsTrue, seed20250601): rng random.Random(seed) items [] for i, q in enumerate(questions, start1): opts list(q[options]) if shuffle_options: rng.shuffle(opts) # 选项乱序防背答案 # 记下原来正确项被打乱后的位置答案页要用 correct next(o[label] for o in opts if o[label] q[answer]) else: correct q[answer] items.append({**q, display_no: i, options: opts, answer_key: correct}) return items把seed存进paper表第二年想复现同一套卷子只要build_paper(questions, seed20250601)就能拿到完全一致的题序。shuffle_options打开时答案页必须用answer_key而不是原始answer字段这两个字段在选项乱序后不再等价。5. 校对与验证答案错位、题号跳号、重复题怎么查自动化组卷跑通之后最后一道防线是校验。人工从头看一遍二十道题的答案对应关系比写校验脚本慢得多也更容易漏。5.1 三道必过的自动校验import re from collections import Counter def check(items): errs [] # 1) 题号连续从 1 到 N 不能有缺口也不能重复 nos [it[display_no] for it in items] if nos ! list(range(1, len(items) 1)): errs.append(f题号不连续或重复: {Counter(nos).most_common(3)}) # 2) 选择题必须恰好有 4 个选项且答案落在选项标签集合内 for it in items: if it[q_type] choice: labels {o[label] for o in it[options]} if len(labels) ! 4: errs.append(f第{it[display_no]}题选项数{len(labels)}) if it[answer_key] not in labels: errs.append(f第{it[display_no]}题答案{it[answer_key]}不在选项内) # 3) 题干不能为空、不能不规范缩进残留 for it in items: if not it[stem].strip() or re.match(r^\s{4,}, it[stem]): errs.append(f第{it[display_no]}题题干异常: {it[stem][:20]!r}) return errs三条检查分别覆盖题号层、选项层、文本层。第二条第 3 项看着不起眼实际上从 PDF 抽出来的题干经常带着首行缩进空格或者页眉页码残留直接渲染到卷面上就是一个孤零零的12靠人工翻页发现不如写死一条正则。5.2 重复题检测字符 shingle 加 Jaccard题库累计几年之后同一道题换个数字反复出现几乎是必然的。用difflib.SequenceMatcher做两两比对在几千道题的规模下太慢改成把题干切成 5 字符的 shingle 集合用 Jaccard 相似度先粗筛。def shingles(text, k5): t re.sub(r\s, , text.lower()) return {t[i:i k] for i in range(max(len(t) - k 1, 1))} def jaccard(a, b): sa, sb shingles(a), shingles(b) return len(sa sb) / len(sa | sb) if sa | sb else 0.0 # 相似度 0.75 以上判定为疑似重复人工确认后再决定是否从题库下架阈值取 0.75 是经验值低于这个数会把「写出下列术语的中文含义」这种通用题干判成重复高于 0.9 又会漏掉只改了数字的题目。计算机专业英语的题干普遍较短实际调参时建议拿已知的几组重复题反推一次。5.3 把校验挂到组卷流程末尾check()返回非空时直接把组卷任务标为失败不要让一份有答案错位的试卷流到打印环节。校验结果连同seed一起写进paper表的一列check_log日后有人质疑某份卷子的题目出处select * from question where id in (...)就能把原始题、原始答案、来源 PDF 文件名全部拉出来对齐。真正让这套流程跑得住的不是解析得多聪明而是每次导出前那几行不肯跳过的校验代码。本文还有配套的精品资源点击获取