简介:这份PDF资料面向备战考研英语的考生及英语教学者,核心解决词汇记忆零散、长难句理解吃力、语法要点难以系统掌握等问题。全书精选考研真题中100个典型句子,通过核心词讲解、同义词与常考搭配归纳,串联起约5500个考研单词,并逐一剖析句子语法结构,帮助读者在语境中记词、在回译中理解句子。资源包共1个PDF文件,大小约3.05MB,内容完整、便于打印或移动端阅读。目前已有756人学习下载,适合需要系统攻克考研词汇与阅读长难句的中高级英语学习者。读者可借助书中句子反复进行英译中、中译英的回译训练,对照原句发现理解盲区,逐步掌握句子结构、语法要点与阅读长难句的方法,从而提升考研英语的词汇量与阅读速度。
1. 从一份 PDF 到一套可复现的词汇训练系统
一份名为「100个句子记完5500个考研单词.pdf」的资料,核心思路并不神秘:用 100 个高度浓缩的长难句,把考研大纲里约 5500 个词全部覆盖进去。它的价值不在于「100 句」这个数字本身,而在于它把「背单词」从孤立记忆变成了语境记忆——每个词都挂在一个有语法结构的句子里,你记的不是词义,而是词的用法。但问题也恰恰出在这里:PDF 是静态的,句子是固定的,而每个人的遗忘曲线、薄弱词分布、复习节奏完全不同。直接拿 PDF 从头读到尾,大概率是前 20 句滚瓜烂熟,后 80 句永远停在收藏夹里。
我见过太多人把这类资料当成「读完就完事」的读物,结果三个月后只记得 abandon。真正能让这份 PDF 产生复利的方式,是把它拆成可检索、可标注、可间隔重复的数据层,再套一个轻量的本地训练流程。这篇文章要讲的,就是怎么把一份静态 PDF 变成一套你能每天跑、能看见进度、能针对薄弱词加练的词汇系统。适合已经有一份类似资料、但苦于「背了忘、忘了背」的考研人,也适合想用脚本把纸质资料数字化的开发者。
2. 把 PDF 拆成句子、词表与索引:数据层怎么建
2.1 先判断 PDF 是文本型还是扫描型
动手之前必须确认一件事:你手里的 PDF 能不能直接提取文字。文本型 PDF 用pdftotext或 Python 的pdfplumber就能拿到干净字符串;扫描型 PDF 本质是图片,必须先走 OCR,否则后面所有步骤都是空中楼阁。判断方法很简单,用pdftotext跑一下,看输出文件大小和内容。
# 文本型 PDF:输出会有可读文字,文件大小通常几十 KB 以上 pdftotext -layout "100个句子记完5500个考研单词.pdf" raw.txt wc -c raw.txt # 如果 raw.txt 只有几百字节或全是乱码,基本就是扫描型 head -c 500 raw.txt逻辑说明:-layout参数保留原始排版,对后续按行切分句子很关键。如果输出为空或乱码,说明是扫描件,需要换 OCR 路线,常见做法是用ocrmypdf先做一层文字层,再走同样的提取流程。参数上,-layout比默认模式更适合句子类资料,因为它不会把多列内容混成一行。
2.2 用正则把 100 个句子切出来
拿到raw.txt后,下一步是定位句子边界。这类资料通常有固定格式:句子编号 + 英文原句 + 中文翻译 + 词汇注释。不同版本排版有差异,但编号模式一般逃不出「数字 + 点/、/空格」这几种。我一般先用正则粗切,再人工校验前 10 条。
import re with open("raw.txt", "r", encoding="utf-8") as f: text = f.read() # 匹配形如 "1." "2、" "03 " 的句子编号开头 pattern = re.compile(r'(?m)^\s*(\d{1,3})\s*[.、\s]\s*(.+?)(?=^\s*\d{1,3}\s*[.、\s]|\Z)', re.S) matches = pattern.findall(text) sentences = [] for idx, body in matches: body = body.strip() if len(body) > 20: # 过滤掉页码、页眉等短噪声 sentences.append({"id": int(idx), "raw": body}) print(f"切出 {len(sentences)} 条") for s in sentences[:3]: print(s["id"], s["raw"][:80])逻辑说明:(?m)开启多行模式,^才能匹配每行开头;(?=...)是前瞻断言,用来在下一个编号出现前截断,避免把两句粘在一起。re.S让.能跨行匹配,因为一个长难句可能占多行。参数上,len(body) > 20这个阈值需要根据实际排版调,太小会混入页码,太大会漏掉短句。切完后一定要打印前几条肉眼确认,正则切分翻车是常态。
2.3 从句子中抽词并去重,建立词表索引
句子切好后,接下来要把每句里的考研词抽出来。这一步不需要完美分词,因为考研词表是已知的——你可以先准备一份大纲词表(常见做法是从公开大纲整理成words.txt,每行一个词),然后用集合匹配的方式标注每个句子覆盖了哪些词。
import re from collections import defaultdict # 加载大纲词表,统一小写 with open("words.txt", "r", encoding="utf-8") as f: vocab = set(w.strip().lower() for w in f if w.strip()) def tokenize(s): return re.findall(r"[a-zA-Z][a-zA-Z\-']+", s.lower()) word_to_sent = defaultdict(list) sent_to_words = {} for s in sentences: tokens = set(tokenize(s["raw"])) hit = tokens & vocab sent_to_words[s["id"]] = sorted(hit) for w in hit: word_to_sent[w].append(s["id"]) # 输出覆盖统计 covered = len(word_to_sent) print(f"词表 {len(vocab)} 词,被句子覆盖 {covered} 词,覆盖率 {covered/len(vocab):.1%}") # 找出没被任何句子覆盖的词,这些是后续要补的缺口 missing = vocab - set(word_to_sent.keys()) print(f"未覆盖 {len(missing)} 词,示例:{list(missing)[:10]}")逻辑说明:tokenize用正则抓英文词,保留连字符和撇号,避免把well-known拆成两个词。word_to_sent是倒排索引,后面做「按词查句」和「按句查词」都靠它。覆盖率统计是关键指标——如果覆盖率低于 90%,说明要么句子没切全,要么词表版本和资料不匹配。未覆盖的词单独存成missing.txt,这是你后续加练的重点。
2.4 落库:用 SQLite 存成可查询的三张表
数据量不大,SQLite 足够。建三张表:sentences存句子,words存词表,links存句子和词的关联。这样后面做查询、标记掌握度、导出 Anki 都方便。
import sqlite3 conn = sqlite3.connect("vocab.db") cur = conn.cursor() cur.executescript(""" CREATE TABLE IF NOT EXISTS sentences ( id INTEGER PRIMARY KEY, raw TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS words ( word TEXT PRIMARY KEY, status INTEGER DEFAULT 0 -- 0 未学 1 模糊 2 掌握 ); CREATE TABLE IF NOT EXISTS links ( sentence_id INTEGER, word TEXT, PRIMARY KEY (sentence_id, word) ); """) cur.executemany("INSERT OR REPLACE INTO sentences(id, raw) VALUES (?, ?)", [(s["id"], s["raw"]) for s in sentences]) cur.executemany("INSERT OR IGNORE INTO words(word) VALUES (?)", [(w,) for w in vocab]) cur.executemany("INSERT OR IGNORE INTO links(sentence_id, word) VALUES (?, ?)", [(sid, w) for sid, ws in sent_to_words.items() for w in ws]) conn.commit() conn.close() print("入库完成")逻辑说明:status字段是后面间隔重复的基础,0/1/2 三档足够轻量。links表的主键设计防止重复插入。入库后你可以直接用 SQL 查「包含某个词的所有句子」,比如SELECT s.raw FROM sentences s JOIN links l ON s.id = l.sentence_id WHERE l.word = 'abandon';。这一步做完,PDF 就从一个死文件变成了可编程的数据源。
3. 用间隔重复把 100 句跑成每日任务:调度层怎么做
3.1 为什么不用现成背词 App 的调度
现成 App 的调度算法是黑匣子,你无法控制「哪些词优先」「句子什么时候出现」。而这份资料的核心资产是句子,不是词。你需要的是「以句子为最小复习单元,但按词掌握度决定句子优先级」的调度。常见做法是自己写一个简化版 SM-2,只保留三个状态和两个间隔参数,够用且透明。
3.2 调度器的核心逻辑与代码
import datetime import sqlite3 conn = sqlite3.connect("vocab.db") cur = conn.cursor() def due_sentences(today, limit=10): # 优先取包含「未学/模糊」词最多的句子 cur.execute(""" SELECT s.id, s.raw, COUNT(*) AS weak_cnt FROM sentences s JOIN links l ON s.id = l.sentence_id JOIN words w ON l.word = w.word WHERE w.status < 2 GROUP BY s.id ORDER BY weak_cnt DESC, RANDOM() LIMIT ? """, (limit,)) return cur.fetchall() def update_word(word, quality): # quality: 0 忘记 1 模糊 2 掌握 cur.execute("SELECT status FROM words WHERE word = ?", (word,)) row = cur.fetchone() if not row: return old = row[0] if quality == 0: new = 0 elif quality == 1: new = max(0, old) # 保持或降级 else: new = min(2, old + 1) cur.execute("UPDATE words SET status = ? WHERE word = ?", (new, word)) conn.commit() today = datetime.date.today().isoformat() tasks = due_sentences(today, limit=10) for sid, raw, cnt in tasks: print(f"[{sid}] 薄弱词 {cnt} 个") print(raw[:120]) print("---")逻辑说明:due_sentences的排序策略是「薄弱词密度优先」,这样你每天先啃最不熟的句子,而不是随机翻。update_word里 quality 为 0 时直接打回未学,为 2 时升一级,最多到 2。参数limit=10是每日句子量,建议从 5 开始,稳定后再加到 10。这个调度器不存复习时间戳,因为句子级复习靠的是词状态驱动,不需要额外的时间维度,简化后反而更容易坚持。
3.3 每日流程:读句、标词、更新状态
实际使用时,流程是:跑脚本拿到今日 10 句 → 逐句精读,遇到标注的词回忆词义 → 在命令行输入每个词的质量评分 → 脚本更新状态。为了减少输入负担,可以做一个交互式循环。
for sid, raw, cnt in tasks: print(f"\n=== 句子 {sid} ===") print(raw) cur.execute(""" SELECT w.word FROM links l JOIN words w ON l.word = w.word WHERE l.sentence_id = ? AND w.status < 2 """, (sid,)) weak_words = [r[0] for r in cur.fetchall()] for w in weak_words: q = input(f" {w} [0/1/2]: ").strip() if q in ("0", "1", "2"): update_word(w, int(q))逻辑说明:只对status < 2的词提问,已掌握的词不再打扰。输入 0/1/2 对应忘记/模糊/掌握,回车跳过。这个循环每天花 15 到 25 分钟,100 句大约 10 到 15 天能过完第一轮,之后进入薄弱词驱动的第二轮。关键是update_word的降级逻辑要保守——模糊不降级,只有明确忘记才打回 0,否则状态会在 0 和 1 之间反复横跳,产生挫败感。
4. 避坑与排查:这套流程最容易翻车的五个地方
4.1 正则切分把两句粘成一句
现象:打印句子时发现第 5 句和第 6 句连在一起,中间没有编号。原因:原文里编号可能用了全角空格或特殊符号,正则没覆盖。解决:把pattern里的[.、\s]扩展为[.、\s\u3000],并在切分后加一条校验——如果某条raw长度超过 800 字符,大概率是粘连,单独打印出来人工处理。
4.2 词表匹配把变形词漏掉
现象:句子里明明有running,但词表里只有run,导致这个词没被标注。原因:正则抓的是原形,没有做词形还原。解决:常见做法是引入nltk的WordNetLemmatizer,对每个 token 做一次还原再匹配。如果不想装依赖,可以维护一张小的变形映射表,覆盖-ing、-ed、-s这几类高频变形。
4.3 SQLite 并发写入报 database is locked
现象:一边跑调度脚本一边手动改状态,偶尔报锁错误。原因:SQLite 默认的写锁是排他的,两个进程同时写会冲突。解决:在连接时加timeout参数,sqlite3.connect("vocab.db", timeout=10),让第二个写入等待而不是直接失败。更稳妥的做法是调度脚本和更新脚本不要同时跑。
4.4 每日任务量设太大导致放弃
现象:第一天设了 30 句,第二天就不想打开了。原因:句子是长难句,精读一句平均 2 到 3 分钟,30 句就是一个半小时,不可持续。解决:把limit降到 5,稳定两周后再加到 8 或 10。这套系统的目标是「每天都能跑」,不是「三天跑完」。
4.5 覆盖率统计虚高但实际没记住
现象:脚本显示覆盖率 98%,但做题时还是不认识。原因:覆盖率只说明词在句子里出现过,不代表你记住了。解决:把status为 2 的词单独导出,定期用SELECT word FROM words WHERE status = 2抽查,随机抽 20 个自测,如果正确率低于 80%,说明状态更新太宽松,需要把 quality 为 1 也纳入降级。
5. 进阶:把词表导出成可打印的薄弱词清单与自测卷
跑完几轮后,你会积累一批status为 0 和 1 的词。这些词才是你真正的提分空间。我一般会写一个导出脚本,生成两份东西:一份是按句子分组的薄弱词清单,用于精读;一份是纯词表自测卷,用于快速筛查。
import sqlite3 conn = sqlite3.connect("vocab.db") cur = conn.cursor() # 导出薄弱词清单,按句子分组 cur.execute(""" SELECT s.id, s.raw, GROUP_CONCAT(w.word, ', ') FROM sentences s JOIN links l ON s.id = l.sentence_id JOIN words w ON l.word = w.word WHERE w.status < 2 GROUP BY s.id ORDER BY s.id """) with open("weak_by_sentence.md", "w", encoding="utf-8") as f: for sid, raw, words in cur.fetchall(): f.write(f"### 句子 {sid}\n\n{raw}\n\n薄弱词:{words}\n\n") # 导出纯词表自测卷,每行一词,右侧留空写词义 cur.execute("SELECT word FROM words WHERE status < 2 ORDER BY RANDOM()") with open("self_test.txt", "w", encoding="utf-8") as f: for (w,) in cur.fetchall(): f.write(f"{w}\t________________\n") print("导出完成")逻辑说明:GROUP_CONCAT把同一句的薄弱词拼成一行,方便打印后对照。自测卷用RANDOM()打乱顺序,避免按字母序产生位置记忆。\t加下划线是给手写留空间,打印出来就是一张可随身带的筛查表。这两份文件配合使用:先做自测卷,把仍然不认识的词在数据库里标回 0,再回到weak_by_sentence.md里精读对应句子。
一个我自己的习惯是每周日晚上跑一次导出,把自测卷打印出来,周一早上通勤时过一遍,把不认识的词用红笔圈出来,晚上回来批量更新状态。这样一周的薄弱词不会超过两页 A4,心理负担很小。这套流程从 PDF 提取到每日调度,核心代码不到 200 行,但能把一份静态资料变成跟着你走的训练系统。希望帮到你。
本文还有配套的精品资源,点击获取