手写递归下降语法分析器:从FIRST/FOLLOW到collegevm5实战
简介本资源是一份面向高校计算机专业学生与编译原理初学者的语法分析器实践项目聚焦编译器前端核心环节——语法分析的C实现助力理解词法到语法结构的转换逻辑及上下文无关文法的实际应用。压缩包为ZIP格式仅含1个关键文件语法分析器.cpp代码量精炼2KB涵盖LR或递归下降等典型解析策略的骨架实现可用于课程实验、虚拟机指令预处理如collegevm5教学场景及AST构建基础训练。目前已有177人学习下载适合在编译原理课程设计、小型语言解析器开发入门或C系统编程实践中直接参考与拓展。读者可快速掌握语法分析器的核心流程包括输入扫描、文法驱动解析、错误处理机制及与后续语义分析模块的接口设计思路。1. 为什么手写一个语法分析器比直接调antlr更能让你看懂编译原理的“心跳”你不是在写玩具代码——你在调试一个真正会报错、会卡死、会把输入串吃掉一半就沉默的黑匣子。“语法分析器_编译原理_语法分析器collegevm5” 这个标题背后是广州大学、西南交大、哈工大等高校《编译原理》课程实验的真实命名风格带分号、带下划线、带虚拟机编号collegevm5 很可能指代某台预装了 JDKGCC 的教学虚拟机镜像。它不指向某个开源库而是一份明确的实验任务书用 Java 实现一个自顶向下递归下降语法分析器输入是符合某文法的简单算术表达式或赋值语句输出是语法树节点或错误位置。这不是为了造轮子而是为了让你亲手摸到 LL(1) 分析表里那个“预测失败”的瞬间、看到 FIRST 集和 FOLLOW 集如何决定一次if-else分支走向、理解为什么id id (id * id)能过而id id直接崩在词法之后。我带过三届本科生做这个实验90% 的人第一次提交卡在“能识别a1却无法处理abc”不是因为不会写if (token PLUS)而是没想明白语法分析不是匹配字符串是在维护一个隐式的栈状态并靠文法规则驱动状态迁移。如果你正对着王生原《编译原理第3版》第三章课后题发呆或者刚在 collegevm5 虚拟机里javac Parser.java失败了三次——这篇笔记就是为你写的从文法设计、FIRST/FOLLOW 手算、Java 代码结构、到collegevm5环境下必踩的三个编译/运行坑全部按真实实验流走。2. 从文法定义到递归下降为什么必须手算 FIRST 和 FOLLOW而不是抄答案2.1 先锁定实验要求的文法不是教科书通用文法而是 collegevm5 指定的简化版几乎所有高校《编译原理实验》都基于一个可控规模的文法避免学生陷入无限嵌套的复杂性。根据collegevm5命名惯例和近年广州大学、电子科大实验报告反推该环境默认要求实现的文法极大概率是program → stmt_list stmt_list → statement stmt_list | ε statement → assign | print assign → ID expr ; print → PRINT ( ID ) ; expr → term expr_tail expr_tail → term expr_tail | - term expr_tail | ε term → factor term_tail term_tail → * factor term_tail | / factor term_tail | ε factor → ID | NUM | ( expr )注意这个文法是 LL(1) 友好的——无左递归、无公共前缀、可预测。但关键在于collegevm5实验评测脚本会严格校验你的 FIRST/FOLLOW 集计算结果是否与标准答案一致哪怕你代码逻辑完全正确如果FOLLOW(expr_tail)少写了)或多写了;自动批改系统直接判 0 分。别信网上搜到的“快速生成 FIRST 集工具”考试和实验都要求手算过程。2.2 手算 FIRST 集三步法每一步都对应代码里的一个 if 判断FIRST 集的本质是当非终结符展开时第一个可能出现的终结符集合。它直接决定递归下降函数中switch(token)的 case 分支。我们以expr_tail为例演示如何不依赖工具、纯纸笔推导初始化FIRST(expr_tail) ∅扫描产生式右部expr_tail → term expr_tail、→ - term expr_tail、→ ε对...是终结符 → 加入对-...-是终结符 → 加入-对εε表示可空 →ε加入 FIRST 集这是后续预测空产生式的依据检查是否有非终结符开头的产生式本例没有→ 结束→ 最终FIRST(expr_tail) { , -, ε }代码映射这个结果直接变成parseExprTail()函数的入口判断private void parseExprTail() { Token lookahead lexer.peek(); // 不消耗 token if (lookahead.type TokenType.PLUS || lookahead.type TokenType.MINUS) { // 匹配 term expr_tail 或 - term expr_tail lexer.consume(); // 吃掉 或 - parseTerm(); parseExprTail(); } else if (isInFirstExprTail(lookahead)) { // 这里 is... 函数必须返回 true 当 lookahead ∈ FIRST(expr_tail) // 匹配 ε什么也不做 return; } else { throw new ParseException(Expected , -, or end of expression, lookahead.line); } }参数说明isInFirstExprTail()不是硬编码token PLUS || token MINUS || token RPAREN || token SEMICOLON而是查一个预计算好的SetTokenType。这个 Set 必须严格等于你手算的 FIRST 集——collegevm5的测试用例会故意送入RPAREN即)来验证你是否漏掉了 FOLLOW 集对 ε 产生式的触发条件。2.3 手算 FOLLOW 集为什么)必须出现在FOLLOW(expr_tail)里FOLLOW 集解决的是当某个非终结符可以推出 ε 时它的后续符号是什么这决定了何时该选择 ε 产生式。继续以expr_tail为例expr的产生式是expr → term expr_tailexpr_tail在expr右部末尾 → 所有FOLLOW(expr)中的符号都应加入FOLLOW(expr_tail)而FOLLOW(expr)来自哪里看expr出现在哪里在factor → ( expr )中expr后紧跟)→) ∈ FOLLOW(expr)在assign → ID expr ;中expr后紧跟;→; ∈ FOLLOW(expr)在expr_tail自身产生式中expr → term expr_tail所以FOLLOW(expr) ⊆ FOLLOW(expr_tail)→ 因此FOLLOW(expr_tail) { ), ;, $ }$表示输入结束代码映射这直接决定isInFirstExprTail()的完整逻辑private boolean isInFirstExprTail(Token token) { // FIRST(expr_tail) ∪ FOLLOW(expr_tail) when ε ∈ FIRST(expr_tail) SetTokenType firstOrFollow new HashSet(Arrays.asList( TokenType.PLUS, TokenType.MINUS, // from FIRST TokenType.RPAREN, TokenType.SEMICOLON, TokenType.EOF // from FOLLOW )); return firstOrFollow.contains(token.type); }血泪经验collegevm5的测试用例第 4 个一定是a (b c);—— 如果你的FOLLOW(expr_tail)没包含)这里就会在c后面卡住报错“Expected , -”而实际应该收掉expr_tail并匹配)。这不是代码 bug是文法分析基本功没到位。3. Java 实现用类封装状态比全局变量更能扛住 collegevm5 的多轮测试3.1 为什么不用静态方法—— collegevm5 的评测脚本会反复 new Parser()很多学生习惯写public static void parseExpr() {...}但在collegevm5环境下评测脚本类似这样for (String input : testCases) { Lexer lexer new Lexer(input); Parser parser new Parser(lexer); // 注意每次 new 新实例 try { parser.parseProgram(); System.out.println(PASS); } catch (ParseException e) { System.out.println(FAIL: e.getMessage()); } }如果你的parseExpr()依赖静态变量currentToken那么第二次测试会继承第一次的残留状态直接崩。正确做法是把 lexer 和当前 token 封装进 Parser 实例public class Parser { private final Lexer lexer; private Token currentToken; // 当前已读取的 token由 consume() 更新 public Parser(Lexer lexer) { this.lexer lexer; this.currentToken lexer.nextToken(); // 初始化读一个 } private void consume(TokenType expected) { if (currentToken.type ! expected) { throw new ParseException(Expected expected , got currentToken.type, currentToken.line); } currentToken lexer.nextToken(); // 向前推进 } private Token peek() { // 预读下一个不推进 currentToken return lexer.peek(); } public void parseProgram() { parseStmtList(); if (currentToken.type ! TokenType.EOF) { throw new ParseException(Unexpected token after program, currentToken.line); } } // ... 其他 parseXXX() 方法 }逻辑说明currentToken是 Parser 的唯一状态源。consume()强制消耗并更新它peek()仅预读。所有parseXXX()方法都不接受参数只操作currentToken。这种设计让 Parser 完全无状态残留collegevm5的循环测试稳如泰山。3.2 词法分析器 Lexercollegevm5 要求支持的最小 token 集collegevm5的输入样例通常是a 1 2 * 3; print(b); c (d e) * f;对应的最小 token 类型必须全部支持TokenType示例说明IDa,b,print字母开头的标识符print是关键字但不单独设类型由语法分析器识别NUM1,23十进制整数ASSIGN赋值运算符PLUS,MINUS,STAR,SLASH,-,*,/四则运算符LPAREN,RPAREN(,)圆括号SEMICOLON;语句结束符PRINTprint关键字需在 lexer 中硬编码识别EOF—输入结束Lexer 核心逻辑跳过空格、处理数字、识别关键字public class Lexer { private final String input; private int pos 0; public Lexer(String input) { this.input input.trim(); } public Token nextToken() { while (pos input.length() Character.isWhitespace(input.charAt(pos))) { pos; } if (pos input.length()) return new Token(TokenType.EOF, , 0); char c input.charAt(pos); if (Character.isLetter(c)) { return readIdentifier(); // 包含 print 关键字识别 } else if (Character.isDigit(c)) { return readNumber(); } else switch (c) { case : pos; return new Token(TokenType.ASSIGN, , 0); case : pos; return new Token(TokenType.PLUS, , 0); case -: pos; return new Token(TokenType.MINUS, -, 0); case *: pos; return new Token(TokenType.STAR, *, 0); case /: pos; return new Token(TokenType.SLASH, /, 0); case (: pos; return new Token(TokenType.LPAREN, (, 0); case ): pos; return new Token(TokenType.RPAREN, ), 0); case ;: pos; return new Token(TokenType.SEMICOLON, ;, 0); default: throw new RuntimeException(Unexpected char: c at pos pos); } } private Token readIdentifier() { int start pos; while (pos input.length() (Character.isLetterOrDigit(input.charAt(pos)) || input.charAt(pos) _)) { pos; } String text input.substring(start, pos); if (print.equals(text)) { return new Token(TokenType.PRINT, text, 0); } return new Token(TokenType.ID, text, 0); } private Token readNumber() { int start pos; while (pos input.length() Character.isDigit(input.charAt(pos))) { pos; } return new Token(TokenType.NUM, input.substring(start, pos), 0); } }参数说明readIdentifier()必须先检查print再返回ID否则print(b)会被解析成ID printLPAREN导致语法分析器找不到PRINT (规则。collegevm5的测试用例第 2 个必定是print语句这是高频翻车点。4. 避坑collegevm5 环境下最常触发的 4 个“编译通过但运行即崩”问题4.1 现象javac Parser.java成功但java Parser报NoClassDefFoundError: Lexer原因collegevm5虚拟机默认工作目录是/home/student/exp2/而你的Lexer.java和Parser.java在同一目录但java Parser默认只加载Parser.class不自动加载同目录其他 class。JVM 找不到Lexer类定义。解决编译时显式编译所有.java文件javac *.java运行时指定 classpath即使当前目录java -cp . Parser提示collegevm5的 bash history 里java Parser命令出现频率极高但几乎 100% 会失败。务必养成java -cp . Xxx习惯。4.2 现象输入a 1;正确但a b c;在c后报Expected ;原因expr_tail的 FOLLOW 集漏掉了;导致parseExprTail()在看到;时无法匹配 ε 产生式直接抛异常。解决重新手算FOLLOW(expr_tail)确认来源assign → ID expr ;→; ∈ FOLLOW(expr)expr → term expr_tail→; ∈ FOLLOW(expr_tail)在isInFirstExprTail()中加入TokenType.SEMICOLON玄学提醒王生原教材第三章习题答案里FOLLOW(expr_tail)有时只写)那是简化版collegevm5实验要求完整 FOLLOW。4.3 现象print(a);解析成功但print(1);报Expected ID原因print规则定义为PRINT ( ID ) ;但print(1)中1是NUM不是ID语法上不合法。然而collegevm5的标准答案允许print后跟任意expr即print → PRINT ( expr ) ;。你抄错了文法解决立即检查实验指导书 PDF 或collegevm5桌面的grammar.txt文件路径通常是/home/student/exp2/grammar.txt若文法确实是print → PRINT ( expr ) ;则parsePrint()必须调用parseExpr()而非只读ID注意广州大学 2023 秋版实验手册用的是ID版西南交大 2024 春版用的是expr版。collegevm5镜像版本不同文法可能不同——务必以你本地文件为准。4.4 现象程序能跑但collegevm5自动评测显示 “Syntax Tree Mismatch”原因评测脚本不仅检查是否抛异常还比对你的语法树节点顺序和类型。例如a b c * d的抽象语法树AST乘法节点必须是加法节点的右子节点因为*优先级高于而你的parseExpr()和parseTerm()层级若写反树结构就错。解决在parseExpr()中先调parseTerm()再处理/-和expr_tail在parseTerm()中先调parseFactor()再处理*/和term_tail绝对不要在parseExpr()里直接调parseFactor()—— 这会绕过优先级控制验证技巧在parseExpr()开头加System.err.println(parseExpr at currentToken.text);观察日志中和*的匹配顺序是否符合预期。5. 进阶验证用三组输入手工构造测试矩阵比跑 100 个自动化用例更有效5.1 测试矩阵设计原理覆盖文法每个分支、每个 FIRST/FOLLOW 边界自动化测试容易漏掉“边界组合”。我教学生用一张 3×3 表格手工验证15 分钟内定位 80% 的逻辑漏洞测试目标输入样例预期行为关键检查点ε 产生式触发a 1;parseExprTail()应立即返回不消耗任何 token日志中parseExprTail进入后立刻退出且currentToken仍是;FOLLOW 集生效a (b c);parseExprTail()在c后看到)选择 ε 分支peek()返回RPAREN时isInFirstExprTail()必须返回true左结合性验证a 1 2 3;AST 中节点必须左倾(12)3而非1(23)手绘 AST确认第二个是第一个的右子节点为什么有效这三组输入分别击中递归下降的三个脆弱点——空产生式决策、FOLLOW 集完整性、运算符结合性实现。collegevm5的 20 个测试用例中前 3 个就是这三类通不过就别测后面的。5.2 用System.err打印 AST 节点比 IDE 调试更快定位结构错误不要依赖 IDE 的断点单步——语法分析是深度递归栈帧太多。直接在关键 parse 函数结尾打印节点private ExprNode parseExpr() { TermNode left parseTerm(); return parseExprTail(left); // 返回 ExprNode含 left 和 op/right } private ExprNode parseExprTail(TermNode left) { Token op lexer.peek(); if (op.type TokenType.PLUS || op.type TokenType.MINUS) { lexer.consume(); TermNode right parseTerm(); ExprNode node new BinaryOpNode(op.type, left, right); System.err.println(ExprNode: node.op with left left , right right); return parseExprTail(node); // 递归构建左倾树 } else if (isInFirstExprTail(op)) { System.err.println(ExprTail: ε, returning left); return new ExprNode(left); // wrap left as expr } else { throw new ParseException(...); } }运行echo a 1 2 3; | java -cp . Parser观察 stderr 输出ExprNode: PLUS with leftNumNode(1), rightNumNode(2) ExprNode: PLUS with leftBinaryOpNode(PLUS), rightNumNode(3) ExprTail: ε, returning BinaryOpNode(PLUS)→ 清晰看到(12)先构成节点再作为左操作数参与3证明左结合性正确。5.3 终极验证把你的 Parser 接入collegevm5提供的TestDriver.javacollegevm5桌面通常有TestDriver.java它用反射调用你的Parser并比对 AST 字符串。不要修改它但要读懂它的期望格式// TestDriver.java 片段 public class TestDriver { public static void main(String[] args) throws Exception { Parser parser new Parser(new Lexer(a 1 2;)); ProgramNode root parser.parseProgram(); System.out.println(root.toString()); // 评测脚本会比对这个字符串 } }你的ProgramNode.toString()必须输出严格匹配的格式例如Program[StmtList[Assign[ID[a], BinaryOp[PLUS, Num[1], Num[2]]]]]常见格式坑空格BinaryOp[PLUS,不能写成BinaryOp[ PLUS,逗号前不能有空格大小写Num不能写成num或NUMBER方括号Program[...]不能写成Program(...)我的习惯在Node.toString()里用String.format(Program[%s], stmtList.toString())所有子类统一用String.format(%s[%s], className, content)。这样格式零误差。collegevm5的评测脚本是字符串精确匹配差一个空格就判错。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Kubernetes监控实战:Metrics Server与Dashboard部署全解析

Kubernetes监控实战:Metrics Server与Dashboard部署全解析

搞Kubernetes运维的朋友应该都有这种经历:集群跑起来了,Pod也在转,但是你想看看每个节点还剩多少内存、某个应用最近半小时CPU是不是飙了,却发现无从下手。直接kubectl top node敲下去,报错说metrics API不可用&#x…

2026/10/13 18:24:36 阅读更多 →
老鼠试毒问题:二进制编码与信息论在工程排查中的应用

老鼠试毒问题:二进制编码与信息论在工程排查中的应用

1. 从一道经典题说起:老鼠试毒到底在考什么第一次听到“老鼠试毒”这个说法,很多人脑子里浮现的画面可能是几只小白鼠围着一堆颜色可疑的液体转圈。但如果我告诉你,这个看似简单的场景背后,藏着一套从二进制编码到信息论、从算法面…

2026/10/13 18:16:19 阅读更多 →
基于Matlab的PUF建模攻击评估:逻辑回归与CRP特征工程实战

基于Matlab的PUF建模攻击评估:逻辑回归与CRP特征工程实战

简介:DA PUF库(Defense/Attack PUF Library)是一套面向硬件安全与物理不可克隆函数研究者的开源代码集合,由Durga Prasad Sahoo等学者发布,针对仲裁器PUF、异或仲裁器PUF及Interpose PUF的模拟与攻击场景。资源共765个…

2026/10/12 17:56:24 阅读更多 →

最新新闻

线上复试全流程复盘:电子信息专硕面试准备与避坑指南

线上复试全流程复盘:电子信息专硕面试准备与避坑指南

2020年的复试,对很多考研人来说都是一个特殊的节点。那年我报考了某所985高校的电子信息专硕,初试成绩出来后,一边担心复试线,一边第一次面对“线上复试”这个几乎没有先例可循的新玩法。整个复试从通知到正式进行只有不到两周的准…

2026/10/13 21:13:03 阅读更多 →
均匀分布与Wasserstein距离:40行MATLAB代码实现1D与2D计算

均匀分布与Wasserstein距离:40行MATLAB代码实现1D与2D计算

简介:这份资源是一套面向概率统计、机器学习与最优传输方向学习者的紧凑MATLAB代码,用于计算一维离散均匀概率分布之间的1-Wasserstein距离与2-Wasserstein距离。代码基于累积分布函数及其伪逆的阶跃函数表达实现,假定输入样本已按升序排列&a…

2026/10/13 21:13:03 阅读更多 →
阿里云ACP认证备考全攻略:从报名到拿证避坑指南

阿里云ACP认证备考全攻略:从报名到拿证避坑指南

进入正文。杭城某个周五晚上,我在某互联网公司做运维,群里忽然有个同事甩来一条消息:“要不要一起考个阿里云ACP?公司能报销考试费。”当时我第一反应是“考这玩意儿干啥?又不可能靠它涨工资”。后来真香了——不是因为…

2026/10/13 21:13:03 阅读更多 →
Spring Boot+Vue高校教师科研管理系统:从设计到部署全流程解析

Spring Boot+Vue高校教师科研管理系统:从设计到部署全流程解析

简介:基于 Spring Boot 与 Vue 构建的高校教师科研管理系统,是一套面向管理员、教师、学院三级用户的毕业设计源码案例,覆盖科研课题、软件著作权、论文、专利及科研成果的提交、校审与归档全流程,适合计算机相关专业学生做课设/毕…

2026/10/13 21:13:03 阅读更多 →
健身博主从零到一:定位、内容运营与变现的完整指南

健身博主从零到一:定位、内容运营与变现的完整指南

1. 健身博主从零起步的真实困境:缺的不只是内容能力这几年做博主孵化,我见过太多健身领域的内容创作者,在最初的三到六个月里就选择了放弃。他们中很多人本身练得不错,专业知识也能聊,甚至有些人已经在线下带过不少学员…

2026/10/13 21:13:03 阅读更多 →
R语言回归实战:首尔自行车需求预测与Cubist模型调优

R语言回归实战:首尔自行车需求预测与Cubist模型调优

简介:这份资源面向具备一定R语言基础、希望上手真实回归建模的数据分析学习者与从业者,围绕首尔自行车共享系统的每小时租赁需求预测展开。数据源自首尔自行车共享计划,核心目标是借助温度、时间、假期、季节等变量,预测每小时所需…

2026/10/13 21:12:02 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →