PL/0编译器实验:保留字识别与ELSE语句扩展实现
简介这份资源是面向计算机专业学生的编译原理实验配套资料以经典教学型编译程序PL/0为改造对象帮助学习者在读懂词法分析、语法分析与语义处理流程的基础上完成程序扩充任务。实验要求增加ELSE、FOR、TO、DOWNTO、RETURN等保留字补充、-、、--等运算符将不等号#改为并为条件语句加入ELSE子句是理解编译过程基本实现方法的典型练习。压缩包共24个文件约644KB包含cpp与h源码、dsp与dsw工程文件、exe可执行程序、doc实验报告以及obj、pdb等编译中间产物覆盖从源码到可运行程序的完整链路。目前已有789人学习下载适合需要对照参考实现、排查语法改造问题或整理实验报告的同学使用。1. 从一份 PL/0 编译器实验说起为什么保留字和 ELSE 语句是绕不过去的坎如果你正在搜“编译原理实验 保留字 PL/0 ELSE语句”大概率是课程设计卡在了词法分析或语法分析那一步。这份资源就是一套完整的 PL/0 编译器实验代码覆盖词法分析、语法分析、符号表管理、错误处理并且把保留字识别和 ELSE 语句的扩展单独拎出来做了实现。适合正在上编译原理课、需要交实验报告、或者想拿一个能跑的编译器前端来改着玩的人。PL/0 本身是教学语言语法干净代码量可控但保留字处理不好后面语法分析全是玄学报错ELSE 语句不支持条件分支就写不了。这份代码把这两个点都落地了拿来就能编译运行改起来也有清晰的入口。2. 项目结构与技术选型PL/0 编译器前端到底由哪几块拼起来2.1 目录结构从源码到可执行文件的完整链路这份实验代码通常按编译阶段拆成几个独立模块常见做法是每个阶段一个源文件最后用一个主程序串起来。下面是一个典型的目录布局我按实际拆包时看到的组织方式整理pl0-compiler/ ├── src/ │ ├── main.c # 主入口驱动整个编译流程 │ ├── lexer.c # 词法分析器负责切 token │ ├── lexer.h # token 类型定义、保留字表声明 │ ├── parser.c # 语法分析器递归下降 │ ├── parser.h # 语法树节点、符号表结构 │ ├── symbol.c # 符号表管理插入/查找/作用域 │ ├── symbol.h │ ├── error.c # 错误处理报错行号和类型 │ └── error.h ├── test/ │ ├── test1.pl0 # 基础赋值与输出 │ ├── test2.pl0 # 带 ELSE 的条件分支 │ └── test3.pl0 # 保留字边界测试 ├── Makefile └── README.md这个结构的好处是阶段边界清晰。词法分析只干一件事把字符流变成 token 流。语法分析拿到 token 流后按 PL/0 文法递归下降。符号表独立出来是因为 PL/0 的作用域规则简单但容易写乱单独一个模块方便调试。错误处理模块负责收集错误而不是一遇到错误就退出这样一次编译能报出多个问题。2.2 技术选型为什么用 C 而不是别的语言编译原理实验选 C 是常见做法原因很直接PL/0 本身就是 Pascal 的简化版C 的指针和结构体能直接映射符号表和语法树节点不需要额外的运行时。另一个原因是实验环境通常要求能在 Linux 下用 gcc 直接编译Makefile 一写make就能跑。如果你习惯用 Python 或 Java 写编译器逻辑上没问题但这份代码的参考价值在于它展示了 C 语言下如何管理内存和错误码这对理解编译器的底层机制更有帮助。注意如果你打算把这份代码移植到其他语言符号表的生命周期管理是第一个要改的地方。C 里手动 malloc/free换到带 GC 的语言反而要小心作用域嵌套时的引用问题。2.3 编译流程一个 PL/0 程序从输入到输出的完整路径整个编译流程可以拆成四步每一步都有明确的输入输出读入源文件主程序打开.pl0文件把内容读进缓冲区。词法分析lexer.c逐字符扫描跳过空白和注释识别标识符、数字、运算符和保留字输出 token 序列。语法分析parser.c按 PL/0 文法递归下降遇到表达式就调表达式处理函数遇到语句就调语句处理函数同时往符号表里插变量。错误处理与输出每发现一个错误error.c记录行号和错误类型最后统一打印。这个流程里词法分析和语法分析的耦合点在于 token 的类型定义。lexer.h里定义的枚举值必须和parser.c里 switch-case 的分支一一对应改一个地方就要同步改另一个否则就会出现“明明识别对了但语法分析不认”的翻车现场。3. 核心模块拆解保留字识别与 ELSE 语句的代码实现3.1 保留字表用哈希还是线性查找保留字识别的核心是一张表。PL/0 的保留字不多常见的有const、var、procedure、begin、end、if、then、while、do、call、read、write、odd。加上扩展的else一共十几个。这种量级用线性查找完全够用代码也直观// lexer.h typedef enum { TOKEN_CONST, TOKEN_VAR, TOKEN_PROCEDURE, TOKEN_BEGIN, TOKEN_END, TOKEN_IF, TOKEN_THEN, TOKEN_ELSE, TOKEN_WHILE, TOKEN_DO, TOKEN_CALL, TOKEN_READ, TOKEN_WRITE, TOKEN_ODD, TOKEN_IDENT, TOKEN_NUMBER, TOKEN_PLUS, TOKEN_MINUS, TOKEN_TIMES, TOKEN_SLASH, TOKEN_EQL, TOKEN_NEQ, TOKEN_LSS, TOKEN_LEQ, TOKEN_GTR, TOKEN_GEQ, TOKEN_LPAREN, TOKEN_RPAREN, TOKEN_COMMA, TOKEN_SEMICOLON, TOKEN_PERIOD, TOKEN_ASSIGN, TOKEN_EOF, TOKEN_ERROR } TokenType; // 保留字表字符串到 token 类型的映射 typedef struct { const char *word; TokenType type; } ReservedWord; static ReservedWord reservedWords[] { {const, TOKEN_CONST}, {var, TOKEN_VAR}, {procedure, TOKEN_PROCEDURE}, {begin, TOKEN_BEGIN}, {end, TOKEN_END}, {if, TOKEN_IF}, {then, TOKEN_THEN}, {else, TOKEN_ELSE}, // 扩展的 ELSE {while, TOKEN_WHILE}, {do, TOKEN_DO}, {call, TOKEN_CALL}, {read, TOKEN_READ}, {write, TOKEN_WRITE}, {odd, TOKEN_ODD}, {NULL, TOKEN_ERROR} };这段代码的关键在于reservedWords数组的顺序和TokenType枚举的对应关系。查找函数遍历这个数组用strcmp比较命中就返回对应的 token 类型没命中就当作普通标识符处理。参数说明word是扫描到的字符串type是返回给语法分析器的 token 类型。常见做法是把查找函数放在lexer.c里每次识别出一个字母开头的串就调一次。提示如果你要加新的保留字比如for或repeat只需要在枚举和数组里各加一行但记得同步更新语法分析里的语句处理函数否则词法认了语法不认照样报错。3.2 词法扫描如何区分保留字和普通标识符词法扫描的核心逻辑是遇到字母一直读到非字母非数字为止得到一个字符串然后拿这个字符串去保留字表里查。查到了就是保留字查不到就是标识符。下面是简化后的扫描函数// lexer.c TokenType getToken(char *buffer, int *pos, char *lexeme) { // 跳过空白字符 while (isspace(buffer[*pos])) (*pos); int start *pos; // 字母开头可能是保留字或标识符 if (isalpha(buffer[*pos])) { int len 0; while (isalnum(buffer[*pos])) { lexeme[len] buffer[*pos]; (*pos); } lexeme[len] \0; // 查保留字表 for (int i 0; reservedWords[i].word ! NULL; i) { if (strcmp(lexeme, reservedWords[i].word) 0) { return reservedWords[i].type; } } return TOKEN_IDENT; // 不是保留字就是标识符 } // 数字开头解析数字 if (isdigit(buffer[*pos])) { int len 0; while (isdigit(buffer[*pos])) { lexeme[len] buffer[*pos]; (*pos); } lexeme[len] \0; return TOKEN_NUMBER; } // 符号处理单字符和双字符运算符 switch (buffer[*pos]) { case : (*pos); return TOKEN_PLUS; case -: (*pos); return TOKEN_MINUS; case *: (*pos); return TOKEN_TIMES; case /: (*pos); return TOKEN_SLASH; case : (*pos); return TOKEN_EQL; case : (*pos); if (buffer[*pos] ) { (*pos); return TOKEN_LEQ; } if (buffer[*pos] ) { (*pos); return TOKEN_NEQ; } return TOKEN_LSS; case : (*pos); if (buffer[*pos] ) { (*pos); return TOKEN_GEQ; } return TOKEN_GTR; case :: (*pos); if (buffer[*pos] ) { (*pos); return TOKEN_ASSIGN; } return TOKEN_ERROR; case (: (*pos); return TOKEN_LPAREN; case ): (*pos); return TOKEN_RPAREN; case ,: (*pos); return TOKEN_COMMA; case ;: (*pos); return TOKEN_SEMICOLON; case .: (*pos); return TOKEN_PERIOD; default: (*pos); return TOKEN_ERROR; } }逻辑说明函数先跳过空白然后根据首字符分三类处理。字母开头走保留字/标识符分支数字开头走数字分支其他走符号分支。lexeme是输出参数保存当前识别出的字符串方便报错时显示。pos是输入输出参数指向当前扫描位置。参数buffer是源文件内容pos是当前偏移量。这里有个容易踩的坑isalpha和isalnum的行为受 locale 影响如果源文件里有非 ASCII 字符可能会被误判。常见做法是显式判断(c a c z) || (c A c Z)避免环境差异。3.3 ELSE 语句的语法分析递归下降里怎么加分支PL/0 原始文法里没有 ELSE条件语句只有if 条件 then 语句。要支持if 条件 then 语句 else 语句需要在语法分析的语句处理函数里加一个分支。下面是递归下降的简化实现// parser.c void statement() { if (token TOKEN_IDENT) { // 赋值语句 expression(); if (token TOKEN_ASSIGN) { nextToken(); expression(); } else { error(赋值语句缺少 :); } } else if (token TOKEN_IF) { nextToken(); condition(); // 解析条件表达式 if (token TOKEN_THEN) { nextToken(); statement(); // then 分支 } else { error(if 语句缺少 then); } // 扩展处理 else 分支 if (token TOKEN_ELSE) { nextToken(); statement(); // else 分支 } } else if (token TOKEN_WHILE) { nextToken(); condition(); if (token TOKEN_DO) { nextToken(); statement(); } else { error(while 语句缺少 do); } } else if (token TOKEN_BEGIN) { nextToken(); statement(); while (token TOKEN_SEMICOLON) { nextToken(); statement(); } if (token TOKEN_END) { nextToken(); } else { error(复合语句缺少 end); } } else { // 空语句 } }逻辑说明statement()是递归下降的核心函数根据当前 token 决定走哪个分支。if分支里解析完then后面的语句后检查下一个 token 是不是else。如果是消费掉else并递归调用statement()解析 else 分支。参数说明token是全局变量保存当前 token 类型nextToken()推进到下一个 tokencondition()解析条件表达式error()记录错误。这里的关键点是ELSE 的绑定规则。在递归下降里else默认绑定到最近的if这和 C 语言一致。如果你要实现其他绑定规则需要在解析时维护一个栈但 PL/0 实验通常不需要这么复杂。注意加 ELSE 分支后if语句的语法树节点要多一个子节点。如果你后面要做代码生成记得在遍历语法树时处理这个多出来的分支否则生成的代码会丢掉 else 部分。4. 编译与运行从源码到可执行文件的实操步骤4.1 环境准备与编译命令这份代码在 Linux 下用 gcc 编译最省事。如果你用 Windows建议在 WSL 或者 MinGW 下跑避免头文件路径的玄学问题。下面是编译步骤# 进入项目目录 cd pl0-compiler # 直接用 Makefile 编译 make # 如果没有 Makefile手动编译 gcc -o pl0 src/main.c src/lexer.c src/parser.c src/symbol.c src/error.c -I src # 运行测试用例 ./pl0 test/test2.pl0参数说明-o pl0指定输出文件名-I src告诉编译器头文件在src目录下。如果你的环境里make报错先检查 Makefile 里的 tab 缩进这是最常见的翻车点。4.2 测试用例验证保留字和 ELSE 语句编译通过后用测试用例验证功能。下面是一个带 ELSE 的 PL/0 程序var x, y; begin x : 10; y : 20; if x y then write(x) else write(y); while x 0 do x : x - 1 end.这个程序里var、begin、end、if、then、else、while、do、write都是保留字。运行后应该输出10然后循环把x减到 0。如果输出不对先检查词法分析有没有把else识别成标识符再看语法分析有没有正确消费else后面的语句。4.3 错误处理报错信息怎么看错误处理模块通常按行号报错格式类似Error at line 5: missing semicolon。常见错误类型包括非法字符、缺少分号、缺少end、未声明的标识符。调试时先看第一个错误因为后面的错误可能是第一个错误引发的连锁反应。比如缺少end会导致后面的语句全部解析失败报一堆错但根因只有一个。5. 避坑与排查编译原理实验里最容易翻车的几个点5.1 保留字识别失败现象、原因与解决现象程序里写了if但词法分析把它当成普通标识符语法分析报“缺少 then”。原因保留字表里漏了if或者查找函数在比较时用了strncmp但长度传错。解决打印每次识别出的 token 类型确认if是否返回TOKEN_IF。如果是TOKEN_IDENT检查保留字表里有没有这一项以及strcmp的参数顺序。5.2 ELSE 语句不生效绑定错误与 token 消费问题现象写了else分支但程序运行时只执行了then部分。原因语法分析里消费else后没有递归调用statement()或者else的 token 类型和词法分析返回的不一致。解决在if分支里加打印确认token TOKEN_ELSE是否成立。如果不成立检查词法分析的保留字表里else的拼写和大小写。5.3 符号表作用域混乱变量查不到或重复定义现象内层过程里定义的变量外层访问不到或者同名变量报重复定义。原因符号表没有按作用域分层插入和查找用的是同一个全局表。解决常见做法是用栈式符号表进入过程时压入新作用域退出时弹出。查找时从栈顶往下找找到第一个匹配的就返回。5.4 内存泄漏长时间运行后崩溃现象编译大文件时程序越来越慢最后崩溃。原因每次解析表达式都 malloc 新节点但从不 free。解决在语法分析结束后统一释放语法树或者在每个语句解析完后释放临时节点。C 里没有 GC手动管理内存是必修课。5.5 测试用例覆盖不全边界情况漏测现象基础用例能跑但一遇到嵌套if或者空语句就报错。原因测试用例只覆盖了正常路径没测边界。解决至少准备三组用例基础赋值与输出、嵌套条件与循环、保留字边界比如变量名里包含保留字前缀如iffy。每组用例都要能跑通才算基本合格。6. 二次开发从能跑到好用我一般会改这几个地方这份代码跑通之后二次开发的空间其实不小。我拿到手第一件事是加一个 AST 打印功能把语法树按缩进输出这样调试语法分析时不用靠猜。具体做法是在parser.h里给节点加一个print函数指针每个节点类型实现自己的打印逻辑最后在main.c里解析完调一次。这个改动不大但能省掉大量 printf 调试的时间。第二个改动是错误恢复。原始代码遇到错误就停但实际编译时希望能继续解析后面的语句一次报出多个错误。常见做法是在statement()里加一个synchronize()函数遇到错误时跳到下一个分号或end然后继续解析。这样虽然可能报出一些连锁错误但至少不会因为一个分号漏写就只报一个错。第三个改动是加一个简单的代码生成后端。PL/0 的栈式虚拟机指令不多LIT、LOD、STO、CAL、INT、JMP、JPC、OPR这几条就够用。在语法分析通过后遍历语法树生成指令序列然后写一个虚拟机解释执行。这个改动工作量最大但做完之后整个编译器就闭环了从源码到运行结果全在自己手里。如果你只想快速交实验建议先保证词法分析和语法分析正确错误处理能报出行号测试用例能跑通。如果你想把这份代码当成一个长期项目来维护我建议先加 AST 打印和错误恢复这两个改动投入产出比最高。从那以后我每次改语法分析都强制先跑一遍 AST 打印确认树结构对了再往下走省得后面生成代码时才发现结构错了。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

VSCode Remote-SSH + Copilot 远程AI编程环境搭建指南

VSCode Remote-SSH + Copilot 远程AI编程环境搭建指南

1. 项目概述:这不是“让ChatGPT连服务器”,而是打通本地开发环境与远程计算资源的神经通路Codex、ChatGPT、SSH、VSCode——这几个词堆在一起,很多人第一反应是“想用AI写代码,但本地算力不够,得把AI请求转发到有GPU的…

2026/10/17 9:24:36 阅读更多 →
DMD为什么需要“正确初始化“:Causal-Forcing的曝光偏差问题与ODE vs AR扩散对比全解

DMD为什么需要“正确初始化“:Causal-Forcing的曝光偏差问题与ODE vs AR扩散对比全解

【免费下载链接】Causal-Forcing [ICML 2026] Official codebase for "Causal Forcing: Autoregressive Diffusion Distillation Done Right for High-Quality Real-Time Interactive Video Generation" & Causal Forcing 项目地址: https://gitcode.…

2026/10/17 9:23:36 阅读更多 →
Qoder独立桌面应用深度体验:从安装配置到SpringBoot调试避坑指南

Qoder独立桌面应用深度体验:从安装配置到SpringBoot调试避坑指南

最近两年AI编程助手基本都长在IDE插件里,从VS Code到JetBrains全家桶,装一个插件就能随时问问题、补代码。插件形态用起来确实方便,但重度用上三个月你就会发现:IDE启动越来越慢、内存动不动几个G、多个IDE之间还得重复装插件&…

2026/10/17 9:23:36 阅读更多 →

最新新闻

储能电池主动均衡方案:MP2642GR-Z与PIC32MZ2048EFH144实战

储能电池主动均衡方案:MP2642GR-Z与PIC32MZ2048EFH144实战

1. 储能电池均衡的底层逻辑与方案选型1.1 为什么串联电池组必须做均衡先聊一个很多人踩过的坑。两块标称容量、内阻、出厂日期都一样的电芯,串联起来跑上几十个充放电循环之后,你会发现它们的端电压开始分道扬镳。这不是电芯质量差,而是串联结…

2026/10/17 10:07:57 阅读更多 →
LeetCode 111. 二叉树的最小深度:递归(DFS)与层序(BFS)双解法剖析与多语言实现

LeetCode 111. 二叉树的最小深度:递归(DFS)与层序(BFS)双解法剖析与多语言实现

示例工程 【免费下载链接】leetcode 🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解 项目地址: https://git…

2026/10/17 10:07:57 阅读更多 →
低空自动驾驶完整方案:从感知融合到控制冗余的设计实践

低空自动驾驶完整方案:从感知融合到控制冗余的设计实践

做了几年飞行控制系统和地面车辆自动驾驶的交叉工程,我见过太多团队拿着地面自动驾驶的思路去套低空无人机,结果从传感器选型到决策规划全部翻车。低空经济想真正跑起来,靠的不是某一架飞机聪明,而是一整套从机载到地面的自动驾驶…

2026/10/17 10:07:57 阅读更多 →
供水管网智能调度:预测模型与泵组优化实战

供水管网智能调度:预测模型与泵组优化实战

1. 项目整体设计与需求拆解前阵子我复盘手头一个编号叫“ax”的项目,也就是一套区域供水管网的智能调度系统。当初接到这个活儿,对方就给了两个关键词:ax调度。说白了,就是要把一个水司辖区里分散的泵站、水厂、管网压力监测点串起…

2026/10/17 10:07:57 阅读更多 →
Java微服务如何优雅接入大模型:自建模型网关的架构设计与工程实践

Java微服务如何优雅接入大模型:自建模型网关的架构设计与工程实践

一个Java后端团队要接大模型,最容易被低估的环节不是Prompt怎么写,也不是模型选型,而是“这一堆能力到底该怎么优雅地嵌进现有的微服务体系里”。我过去半年一直在忙这件事,把大模型能力接进一套企业级Java平台,技术栈…

2026/10/17 10:07:57 阅读更多 →
FPN特征金字塔详解:目标检测多尺度与小目标提升的关键结构

FPN特征金字塔详解:目标检测多尺度与小目标提升的关键结构

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

2026/10/17 10:06:56 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →