本文介绍在逐行处理文本时,使用 spaCy Matcher 提取匹配实例、当前句子及前一句的完整实现方案,核心在于维护跨句子的上下文状态(如 prev_sent 变量),避免因句子无索引而无法回溯的问题。
本文介绍在逐行处理文本时,使用 spacy matcher 提取匹配实例、当前句子及前一句的完整实现方案,核心在于维护跨句子的上下文状态(如 `prev_sent` 变量),避免因句子无索引而无法回溯的问题。
在使用 spaCy 的 Matcher 进行规则匹配时,若需同时获取「匹配片段」「所在完整句子」和「前一句」,关键难点在于:spaCy 的 doc.sents 是生成器,句子对象本身不携带位置索引,也无法直接通过 sent[-1] 获取上一句(该写法实际访问的是句子内最后一个 token)。正确解法是在遍历句子时主动维护一个指向“上一句”的引用变量,并在每次匹配成功时读取该引用。
以下为优化后的完整实现流程:
- matcher 初始化一次,复用提升性能:将 Matcher 实例创建移至文件循环外,避免重复构建;
- 逐句遍历 + 状态追踪:对每个 doc 调用 doc.sents,用 prev_sent 缓存上一个句子对象;
- 安全访问前一句:仅当 prev_sent is not None 时取 prev_sent.text,否则填充 "N/A" 或空字符串;
- 精准匹配范围:matcher(sent) 直接作用于句子级 Span,确保匹配逻辑与语境一致(而非整行文档)。
import spacy
from spacy.matcher import Matcher
import pandas as pd
nlp = spacy.load("en_core_web_sm")
pattern = [{"LOWER": "error"}, {"POS": "VERB"}] # 示例:匹配 "error occurred" 类模式
matcher = Matcher(nlp.vocab)
matcher.add("ERROR_PATTERN", [pattern])
instances = []
with open('file.txt', 'r') as f:
for line in iter(f.readline, ''):
doc = nlp(line.strip()) # 去除换行符,避免空句干扰
prev_sent = None
for sent in doc.sents:
matches = matcher(sent)
if matches:
for match_id, start, end in matches:
matched_span = sent[start:end]
instances.append(pd.Series({
"instance": matched_span.text,
"sentence": sent.text.strip(),
"previous_sentence": prev_sent.text.strip() if prev_sent else "N/A"
}))
prev_sent = sent # 更新为当前句,供下一轮使用
df = pd.DataFrame(instances)
print(df[["instance", "sentence", "previous_sentence"]])
⚠️ 注意事项:
- 单行即单文档假设:本方案默认每行是独立语义单元(如日志条目、对话轮次)。若需跨行连贯句子分割(如段落级断句),应先合并多行为完整文本再调用 nlp(),并改用 doc[i-1:i] 等索引方式定位前句;
- 空句/标点边界问题:doc.sents 依赖模型内置断句规则,对简短或格式异常的行(如纯数字、无标点)可能失效,建议预处理或添加 if len(list(sent)) > 0: 校验;
- 性能考量:若文件极大,可考虑使用 nlp.pipe() 批量处理,但需注意 sents 迭代逻辑需适配批处理结构。
该方法简洁、鲁棒且符合 spaCy 最佳实践,适用于日志分析、合规审查、问答上下文提取等需局部语境感知的 NLP 场景。