正则提取固定模式关系需限定上下文、约束实体名、白名单谓词;示例匹配”classs+(w+)s:s(w+)”提取继承关系,避免宽泛模式误抓描述性语句。

用 Regex 快速提取固定模式的关系(比如 “A 是 B 的父类”)
如果你面对的是格式相对规整的注释、文档或日志,比如 "class Derived : Base" 或文本中明确出现 "继承自"、"依赖于" 这类词,正则就是最直接的工具。它不解决语义,但能稳住第一批可落地的关系。
常见错误是写太宽泛的模式,比如 @"(w+)s+是s+(w+)s+的s+(w+)",结果把 "String 是不可变的" 也抓进来——这不是实体关系,是描述性语句。
- 限定上下文:只在
///注释块或/* ... */中运行,避开代码正文和字符串字面量 - 实体名加约束:用
[A-Za-z_]w*替代w+,排除数字开头或纯数字匹配 - 动词必须白名单:只匹配
"继承自|实现|依赖|调用|包含|属于"等你实际关心的谓词,别用.*模糊兜底 - 示例:提取接口实现关系
Regex.Match(line, @"classs+(w+)s*:s*(w+w*>?)"),注意要处理泛型如IEnumerable<t></t>中的和 <code>>转义
Microsoft.CodeAnalysis 解析 AST 提取真实代码级关系
真正可靠的类继承、方法调用、字段访问关系,只能从编译器抽象语法树(AST)里挖。靠文本扫描永远绕不开字符串拼接、宏展开、条件编译这些坑。
容易踩的坑是直接用 SyntaxTree.GetRoot() 后暴力遍历所有 IdentifierNameSyntax,结果把变量名、命名空间、字符串内容全当“实体”拎出来——没类型上下文,关系就无意义。
- 优先走语义模型:
semanticModel.GetSymbolInfo(node).Symbol才能确认这个BaseClass确实是个类型,而不是局部变量名 - 关注三类关键节点:
BaseListSyntax(继承/实现)、InvocationExpressionSyntax(调用)、MemberAccessExpressionSyntax(成员访问) - 别忽略别名:如果用了
using Alias = Some.Long.Namespace.Type;,Alias.Method()的符号解析必须通过Compilation全局作用域,不能只看当前文件 - 性能提示:一次性加载整个解决方案比逐个文件解析快得多,
CSharpCompilation.Create()配合ParseOptions.WithPreprocessorSymbols()处理条件编译
用 NLP.NET 或 ML.NET 做轻量级语义关系分类(非代码文本)
当你面对的是 XML 注释、XML 文档、设计文档或 PR 描述这类自然语言文本,且需要识别像 “UserRepository 负责持久化 User 实体” 这种隐含关系时,规则已不够用,得上简单 NLP。
别一上来就拉大模型——ML.NET 的 TextFeaturizingEstimator + SdcaMaximumEntropyMulticlassTrainer 在百条样本下就能对 “调用/拥有/配置/触发” 几类关系做到 75%+ 准确率,够用且不拖慢构建流程。
- 训练数据必须带实体边界:标注出
[UserRepository]和[User],否则模型学不会谁是主语谁是宾语 - 特征别只喂词袋:加入依存句法关键词距(如 “负责” 到最近名词的距离)、实体类型(类名/方法名/配置项)作为数值特征
- 兼容性注意:
NLP.NET不维护了,ML.NET的文本管道在 .NET 6+ 上需引用Microsoft.ML.Text单独包,不是默认内置 - 示例 pipeline:
mlContext.Transforms.Text.FeaturizeText("Features", "Text")后接二分类器判断 “是否存在 ‘配置’ 关系”
关系去重与可信度打标(避免 A→B 和 B←A 重复入库)
不同路径提取的关系会大量重叠:AST 说 Program 调用 Console.WriteLine,正则又从注释里扫出 “Program 输出日志”,这两条该合并还是丢弃?不处理,图谱就长毛。
最易被忽略的是关系方向性丢失。比如 typeof(A).GetInterfaces() 返回 IInterface,这表达的是 A 实现 IInterface,不是 IInterface 被 A 实现——箭头反了,下游推理全错。
- 统一用 (subject, predicate, object) 三元组结构存储,subject/object 必须是完整符号名(如
"MyApp.Services.UserService"),不是简称 - 为每条关系挂来源标签:
"ast:inheritance"、"regex:comment-dependency"、"nlp:doc-owns",方便后续按可信度加权 - 同 subject+object 但 predicate 冲突时(如 “继承” vs “组合”),保留 AST 来源,丢弃 NLP 来源——代码事实永远优先于文档推测
- 调试技巧:导出为 CSV 时加一列
confidence_score,AST 关系设为 0.95,正则设 0.7,NLP 设 0.6,一眼看出哪条该人工复核
事情说清了就结束。关系提取最难的从来不是怎么找,而是怎么让不同来源的结果在同一个坐标系里对齐——实体消歧、方向判定、置信度归一,这些不写进 pipeline,后面建模全在还债。