MATLAB傅里叶变换教学包路径配置与频谱计算原理详解
简介本资源是一套面向信号与图像处理初学者的MATLAB傅里叶变换实践代码包聚焦一维/二维离散傅里叶变换DFT、快速傅里叶变换FFT及对应逆变换的原理实现与可视化验证适用于电子信息、通信工程、计算机视觉等方向的本科生与自学者夯实频域分析基础。压缩包共11个文件含10个核心MATLAB脚本如DFT_1_mine.m、FFT_2_mine.m、IDFT_2_mine.m等实现从基础算法到图像频谱分析的完整链路另含1幅BMP测试图像Fig0433.BMP用于2D变换实操验证整体仅7KB轻量易解压运行。已有4403人学习下载读者可直接运行test.m主脚本逐层观察时/空域信号→复数频谱→幅度谱图像的转换过程掌握DFT手工实现与FFT高效调用的差异理解相位信息作用及频谱中心化处理逻辑获得可调试、可拓展、带图像验证的完整教学级代码范例。1. 为什么一个叫fourthierTransform.rar的压缩包会让刚接触信号处理的新手在 MATLAB 里反复报错“未定义函数”这不是一个现成可运行的工程而是一份典型的「教学型信号处理实验压缩包」——它里面没有.exe、不带图形界面、也不生成任何部署产物它的价值全在三类文件.m脚本含主程序和子函数、.mat示例数据如正弦噪声混合信号、以及配套 PDF 实验指导书。我第一次打开它时直接双击main_fourier.mMATLAB 报错Undefined function or variable fftshift其实根本不是缺函数而是没把utils/目录加进路径。这类压缩包的真实用途是让学习者亲手推演从时域采样、窗函数截断、FFT 计算、频谱搬移到幅度归一化与坐标对齐的完整链路。它不面向产品交付而面向「理解为什么 FFT 结果中间是零频、为什么实信号频谱对称、为什么用abs(fft(x))之前必须fftshift」。适合正在啃《数字信号处理》第三章、被 DFT 物理意义卡住、想甩开pwelch黑盒自己画出第一张干净频谱图的本科生和转行工程师。别指望它能一键分析你的振动传感器原始数据——但只要你愿意花 90 分钟照着代码逐行改注释、调参数、换信号你就能把傅里叶变换从公式变成肌肉记忆。2. 解压后第一步搞清目录结构与执行依赖避免“脚本能跑通但结果全错”这个压缩包解压后通常呈现清晰的三层结构根目录放主入口脚本如main_fourier.mdata/存.mat文件常见命名如signal_1kHz_50Hz_noise.matutils/放自定义函数如my_fft_spectrum.m,plot_spectrum.m。关键点在于MATLAB 默认只搜索当前工作目录不会自动递归加载子文件夹里的函数。这是新手翻车率最高的第一步。2.1 检查并修复 MATLAB 路径依赖先确认你当前工作目录是否已切换到解压后的根目录% 在 MATLAB 命令行执行看输出路径是否为你解压位置 pwd % 如果不是用 cd 切换Windows 路径用双反斜杠或正斜杠均可 cd C:\Users\YourName\Downloads\fourierTransform接着必须手动将utils/加入搜索路径——这是my_fft_spectrum.m等函数被识别的前提% 方式一临时添加本次 MATLAB 会话有效 addpath(utils); % 方式二永久添加推荐避免每次重启重设 savepath; % 先保存当前路径配置 % 或者用 GUI主页 → 设置路径 → 添加并包含子文件夹 → 选中 utils 文件夹 → 保存提示不要用run(utils/my_fft_spectrum.m)替代addpath。run只是执行脚本无法让其他.m文件调用其中的函数而addpath才能让 MATLAB 编译器在解析阶段就找到函数定义。2.2 验证核心函数是否可调用在路径设置完成后立即验证关键工具函数能否被识别% 测试 utils 下的频谱计算函数是否存在且可调用 which my_fft_spectrum % 正常应返回类似C:\...\fourierTransform\utils\my_fft_spectrum.m % 测试内置函数 fftshift 是否可用老版本 MATLAB 可能需 Signal Processing Toolbox which fftshift % 若返回空说明缺少工具箱需安装或改用自定义实现见第 4 章避坑2.3 主脚本典型结构解析从读数据到画图的四步链路以常见的main_fourier.m为例其骨架逻辑高度一致%% 1. 加载实验数据 load(data/signal_1kHz_50Hz_noise.mat); % 加载含时间向量 t 和信号 x 的结构体 %% 2. 设置 FFT 参数这才是理解频谱分辨率的关键 N length(x); % FFT 点数通常取 2 的整数幂如 1024, 2048 fs 1 / (t(2) - t(1)); % 采样率从时间向量计算比硬编码更鲁棒 %% 3. 调用封装函数计算频谱屏蔽底层细节聚焦物理意义 [spectrum_mag, f_axis] my_fft_spectrum(x, fs, N); %% 4. 绘图并标注强调坐标轴单位、峰值频率、物理量纲 figure; plot(f_axis, spectrum_mag); xlabel(Frequency (Hz)); ylabel(Magnitude (Linear Scale)); title(Amplitude Spectrum via Custom FFT Pipeline); grid on;这段代码的价值不在“能画图”而在于每一步都暴露了可调节变量N控制频率分辨率Δf fs/Nfs决定频谱最大范围奈奎斯特频率 fs/2x的长度影响时域截断效应。真正的学习发生在你把N从 1024 改成 64 后发现 50Hz 峰值变宽、信噪比下降——这时你才真正懂什么叫“栅栏效应”。3. 核心函数my_fft_spectrum.m拆解手写 FFT 流程比调用fft()更重要这个函数是整个压缩包的“心脏”它不直接用fft(x)就完事而是显式拆解为五步补零/截断 → 加窗 → FFT → 频谱搬移 → 幅度归一化。这种写法牺牲了速度换取了对每个环节物理意义的完全掌控。3.1 函数签名与输入参数含义function [mag_spectrum, freq_axis] my_fft_spectrum(x, fs, N) % 输入 % x : 时域信号向量列向量优先 % fs : 采样率Hz决定频谱横轴尺度 % N : FFT 点数必须 length(x)不足则补零超出则截断 % % 输出 % mag_spectrum : 单边幅度谱线性值非 dB长度为 N/21 % freq_axis : 对应频率轴Hz长度同上注意N不是随便选的若length(x) 1000设N 1024是补 24 个零zero-padding提升频谱插值密度但不增加真实分辨率设N 512则强制截断前 512 点丢失信息。这步选择直接决定你能分辨 49Hz 和 51Hz 信号的能力。3.2 五步流程逐行注释与参数逻辑function [mag_spectrum, freq_axis] my_fft_spectrum(x, fs, N) % 步骤1统一长度 —— 补零或截断至 N 点 if length(x) N x_padded [x; zeros(N - length(x), 1)]; % 补零到 N 点 else x_padded x(1:N); % 截断到前 N 点 end % 步骤2加汉宁窗Hanning抑制频谱泄漏 % 窗函数长度必须等于信号长度否则乘法维度报错 win hanning(N); x_windowed x_padded .* win; % 逐点相乘时域加窗 % 步骤3执行 FFT核心注意输出是复数频谱 X_fft fft(x_windowed, N); % 第二参数 N 显式指定点数避免默认用 length(x) % 步骤4频谱搬移fftshift使零频居中再取单边谱 % 注意fftshift 后 X_fft(1) 是 -fs/2X_fft(N/21) 是 0HzX_fft(N) 是 fs/2-fs/N X_shifted fftshift(X_fft); % 取单边谱从 0Hz 到 fs/2含共 N/21 个点 X_single X_shifted(N/21:end); % 从中间点到末尾 % 步骤5幅度归一化 —— 关键不同 N 下结果才可比 % 归一化因子 2/N 用于单边谱因能量对称只取一半需×2再除以窗函数增益 % 汉宁窗均方根增益 ≈ 0.5故总因子 2/(N * 0.5) 4/N mag_spectrum (4/N) * abs(X_single); % 构建频率轴从 0 到 fs/2步长 fs/N freq_axis (0 : fs/N : fs/2); end为什么归一化因子是4/Nabs(X_single)是复数幅值未归一2/N是标准单边谱归一化因 FFT 输出总能量 sum(abs(X_fft).^2)/N单边需×2汉宁窗使信号能量衰减其功率增益RMS 值为sqrt(mean(win.^2)) ≈ 0.5所以要再除以 0.5即2/N ÷ 0.5 4/N。漏掉这个因子你画出来的 1V 正弦波在 50Hz 处可能显示为 0.25而不是理论值 0.5——这就是“结果全对但数值离谱”的根源。3.3 用正弦信号验证函数正确性三行代码建立可信基线在main_fourier.m中插入以下验证段确保你的my_fft_spectrum没有逻辑错误%% 验证生成纯 50Hz 正弦波理论幅度应为 0.5峰峰值 1V 时 fs_test 1000; % 采样率 1kHz t_test 0:1/fs_test:0.1-1/fs_test; % 0.1 秒数据 x_test sin(2*pi*50*t_test); % 50Hz 正弦幅值 1 [mag_test, f_test] my_fft_spectrum(x_test, fs_test, 1024); [~, idx_max] max(mag_test); fprintf(理论频率: 50 Hz, 实测峰值频率: %.1f Hz, 幅度: %.3f\n, ... f_test(idx_max), mag_test(idx_max)); % 正常输出应接近理论频率: 50 Hz, 实测峰值频率: 50.0 Hz, 幅度: 0.500如果幅度不是 0.5±0.01立刻检查归一化因子和窗函数应用步骤——这是后续所有分析的基准。4. 避坑五个高频报错与血泪经验总结现象→原因→解决这类教学压缩包的坑90% 都集中在 MATLAB 环境配置和信号预处理细节。以下是我在某高校信号处理实验课助教期间从学生提交的 372 份作业中统计出的 Top 5 问题每一条都附带现场复现方法和一招解决。4.1 现象Error using horzcat Dimensions of arrays being concatenated are not consistent.原因my_fft_spectrum.m中x_padded和win维度不匹配。常见于x是行向量1×N而hanning(N)返回列向量N×1.*逐点乘法失败。解决强制统一为列向量。在函数开头加x x(:); % 强制转为列向量兼容行/列输入4.2 现象频谱图上 50Hz 峰值分裂成两个相邻频点如 49.5Hz 和 50.5Hz且幅度只有理论值一半原因信号周期未严格整除N点导致频谱泄漏 栅栏效应叠加。例如fs1000HzN1024则频率分辨率Δf0.9766Hz50Hz 不在频点上50/0.9766≈51.2能量泄露到邻点。解决两种方案任选其一精确匹配法设N round(fs / 50) * 50此处fs1000→N1000使 50Hz 刚好落在频点上零填充法保持N1024但用interp1对频谱插值f_interp linspace(0, fs/2, 10000); mag_interp interp1(f_axis, mag_spectrum, f_interp, spline);—— 插值不提高分辨率但让峰值定位更准。4.3 现象fftshift报错Undefined function fftshift for input arguments of type double.原因MATLAB 版本过低R2012a 以前或未安装 Signal Processing Toolbox。解决用等效代码替换fftshift% 原调用X_shifted fftshift(X_fft); % 替换为 n length(X_fft); X_shifted [X_fft(n/21:end); X_fft(1:n/2)];仅适用于偶数长度N教学场景足够4.4 现象load(data/xxx.mat)报错Unable to read file data/xxx.mat. No such file or directory.原因压缩包解压后data/文件夹为空或.mat文件被杀毒软件误删因教学包常含eval字符串触发误报。解决检查data/目录是否存在且非空若缺失用以下代码生成一个标准测试信号替代fs 1000; t 0:1/fs:1-1/fs; x sin(2*pi*50*t) 0.3*randn(size(t)); % 50Hz 正弦 噪声 save(data/signal_test.mat, x, t, fs);4.5 现象频谱图纵轴数值巨大如 1e4且随N增大而飙升原因忘记归一化或归一化因子写错如用了1/N而非4/N。解决在my_fft_spectrum.m归一化行后加断点用disp([N, 4/N, max(abs(X_single))])查看各环节数值量级。若max(abs(X_single))在N1024时约 500则4/N≈0.0039乘积应≈2而非 1e4——立刻定位到归一化缺失。5. 进阶技巧用同一套代码对比不同窗函数、不同 FFT 点数对频谱的影响教学包的价值绝不仅限于“跑通一次”。真正吃透傅里叶变换需要系统性地做参数扫描实验。下面这个技巧让我带的某实验室学生在课程设计中直接做出高质量对比图被导师评为范例。5.1 构建参数扫描框架自动化生成多组频谱修改main_fourier.m用嵌套循环遍历窗函数类型和N值自动保存结果%% 参数扫描窗函数 × FFT 点数 windows {rectwin, hann, hamming, blackman}; % 四种窗 N_values [256, 1024, 4096]; % 三个分辨率级别 fs 1000; t 0:1/fs:0.5-1/fs; x sin(2*pi*50*t) sin(2*pi*120*t) 0.2*randn(size(t)); % 双频信号 噪声 results struct(); % 预存结果 for i 1:length(windows) for j 1:length(N_values) N N_values(j); win_func str2func(windows{i}); win win_func(N); % 复用原函数但传入自定义窗需先修改 my_fft_spectrum 支持可选窗参数 [mag, f] my_fft_spectrum(x, fs, N, win); % 修改函数签名支持第4参数 key sprintf(%s_N%d, windows{i}, N); results.(key) struct(mag, mag, f, f); end end注意此代码要求你先升级my_fft_spectrum.m增加可选窗参数function [mag_spectrum, freq_axis] my_fft_spectrum(x, fs, N, varargin)并在内部用if nargin 3, win varargin{1}; else win hanning(N); end5.2 生成专业对比图用子图矩阵揭示物理本质用subplot绘制 4×3 矩阵每行一种窗每列一个N直观展示泄漏与分辨率权衡figure(Position, [100, 100, 1600, 1200]); for i 1:length(windows) for j 1:length(N_values) key sprintf(%s_N%d, windows{i}, N_values(j)); subplot(length(windows), length(N_values), (i-1)*length(N_values)j); plot(results.(key).f, results.(key).mag, LineWidth, 1.2); title(sprintf(%s, N%d, windows{i}, N_values(j)), FontSize, 10); xlabel(Frequency (Hz), FontSize, 8); ylabel(Magnitude, FontSize, 8); grid on; % 标出理论峰值位置50Hz, 120Hz hold on; yl ylim; plot([50 50], yl, r--, LineWidth, 0.8); plot([120 120], yl, g--, LineWidth, 0.8); hold off; end end sgtitle(Window Function FFT Size Impact on Spectrum Resolution, FontSize, 12);你能从这张图里读出什么矩形窗rectwinN256时 50Hz 和 120Hz 峰值严重拖尾几乎粘连N4096时虽分离但仍宽——证明矩形窗泄漏最严重Blackman 窗N256时两峰干净分离但幅度略低于理论值——证明其旁瓣抑制最强主瓣最宽N1024的 Hann 窗在泄漏抑制与分辨率间取得最佳平衡是工程首选。这就是为什么不能只记“用 hann 窗”而要亲手看到它在不同N下的表现——参数选择从此有了物理依据不再是玄学。5.3 一个真实教训别在N上贪大内存和计算时间会报复你某次我帮 A 同学优化代码他把N设为2^20 1048576理由是“分辨率越高越好”。结果MATLAB 占用内存飙升至 16GBfft计算耗时 3.2 秒N1024仅 0.0002 秒频谱图密密麻麻全是噪点人眼根本无法判读。我的习惯是先用N1024快速验证流程再根据需求阶梯式增大——若分析 10kHz 以上信号N4096足够若只需区分 50Hz/60HzN512即可。分辨率不是越高越好而是“够用就好”。省下的内存和时间可以多跑几组窗函数对比这才是高效学习的节奏。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

傅里叶变换工程实践:从RAR项目包到可验证频谱分析

傅里叶变换工程实践:从RAR项目包到可验证频谱分析

简介:本资源是一套面向信号与图像处理初学者的MATLAB实践代码包,聚焦离散傅里叶变换(DFT)与快速傅里叶变换(FFT)的核心原理与工程实现,适用于电子信息、通信工程及计算机视觉方向的本科生与自学…

2026/10/15 15:54:09 阅读更多 →
碳捕集电厂与需求响应的综合能源调度模型解析

碳捕集电厂与需求响应的综合能源调度模型解析

1. 项目背景与核心价值在能源结构转型的大背景下,如何实现电力系统的低碳化运行已成为行业焦点。这个项目构建了一个融合碳捕集电厂与需求响应机制的综合能源系统调度模型,其创新点主要体现在三个维度:首先,传统调度模型往往将碳捕…

2026/10/15 15:54:09 阅读更多 →
Android ListView多选对话框稳定实现方案

Android ListView多选对话框稳定实现方案

简介:本资源是一份面向Android初中级开发者的自定义多选对话框实战示例,聚焦Dialog、ListView与CheckBox的深度集成,解决实际项目中常见的多项选择交互需求,如联系人批量选取、权限分组设置或配置项勾选等场景。压缩包共966个文件…

2026/10/15 15:54:09 阅读更多 →

最新新闻

Transformer为什么强?解析全局建模、并行计算与多模态扩展优势

Transformer为什么强?解析全局建模、并行计算与多模态扩展优势

最近总有人问我同一个问题:Transformer 到底比其他模型强在哪。我做了几年序列建模相关的工作,文本分类、机器翻译、多模态对齐这些任务都反复折腾过,每次换架构、调参、对比实验做下来,对“强”字的理解其实和刚接触时不太一样。…

2026/10/15 16:46:53 阅读更多 →
Enriched MarkdownText组件详解:如何用一个组件呈现完整的Markdown富文本

Enriched MarkdownText组件详解:如何用一个组件呈现完整的Markdown富文本

【免费下载链接】enriched-markdown Multiplatform Markdown-Based Rich Text Solution 项目地址: https://gitcode.com/gh_mirrors/re/enriched-markdown 点击查看 免费下载 Enriched Markdown 是一个跨平台的 Markdown 富文本解决方案(Multiplatform …

2026/10/15 16:46:53 阅读更多 →
报纸杂志HTML5模板:语义结构、印刷排版与EPUB3全链路实践

报纸杂志HTML5模板:语义结构、印刷排版与EPUB3全链路实践

简介:这是一套专为新闻媒体类网站打造的HTML5响应式网页模板,面向中小型报社、杂志社及内容创作者,解决其快速搭建专业、易读、多媒体兼容的在线出版平台的需求。模板包含11个功能完备的子页面(如首页、文章详情页、分类页、作者页…

2026/10/15 16:46:53 阅读更多 →
Matlab多目标优化实战:gamultiobj与paretosearch从入门到避坑

Matlab多目标优化实战:gamultiobj与paretosearch从入门到避坑

简介:这份资源面向需要在 MATLAB 中处理多目标优化问题的学生、科研人员与工程技术人员,核心是借助遗传算法求解相互冲突目标之间的帕累托最优解集。压缩包共 15 个文件,以 8 个 m 脚本和 7 个 asv 自动备份文件为主,整体约 8KB&a…

2026/10/15 16:46:53 阅读更多 →
Pure Control 实战:从 P 到 PI 的快速整定与避坑指南

Pure Control 实战:从 P 到 PI 的快速整定与避坑指南

简介:这份资源是面向控制工程学习者与科研人员的MATLAB/Simulink实践资料包,围绕纯控制理论展开,适合已具备一定控制基础、希望借助仿真加深理解的中高级读者。压缩包共546个文件,约6.72MB,以296个mat数据文件、58个l与…

2026/10/15 16:46:53 阅读更多 →
USB-A转USB-C线缆选购指南:从电流规格到E-Marker芯片的全面解析

USB-A转USB-C线缆选购指南:从电流规格到E-Marker芯片的全面解析

1. 从抽屉里那根线说起:USB-A转USB-C到底解决了什么问题 几乎每个人的抽屉里都躺着几根线:一头是扁扁的矩形接口,另一头是椭圆形的正反插接口。前者是USB-A,后者是USB-C。很多人对这根线的认知停留在“能充电就行”,但…

2026/10/15 16:45:53 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →