C++调用7z解压缩封装:从SDK到可调用接口的完整指南
简介这是一份面向C开发者的7z解压缩封装代码资源核心思路是借助bit7z与7z.dll完成压缩包调用并重点解决解压进度获取不便的问题适合需要在桌面端或工具类项目中集成压缩解压能力、又希望实时反馈进度的中高级开发者参考。资源包共收录1362个文件整体约4.83MB其中以525个h头文件、405个cpp源文件、100个c文件为主构成核心实现与依赖库另有42个rc资源脚本、40个cs与38个hpp文件、28个java文件以及makefile、dsp、dsw、sln、vcxproj等工程构建文件方便在不同编译环境下直接组织项目。内容还包含asm汇编优化、def导出定义、lib静态库、dll动态库与ico图标等配套素材目录结构完整。目前已有2372人学习下载读者可从中获得可复用的封装接口、进度回调实现思路与工程配置范例减少自行对接7z.dll的调试成本。1. 从一次打包翻车说起C 调用 7z 解压缩到底在解决什么上周帮一个做桌面工具的朋友排查问题他的程序需要把用户丢进来的.7z压缩包解开结果用系统自带的解压库折腾了两天遇到带密码的包直接卡死遇到分卷包又报错最后定位到根因他用的那套库根本不支持 7z 的完整格式。这不是个例C 生态里能原生处理 7z 的库本来就少libarchive 对 7z 的支持有限zlib 只管 deflate真要完整支持 7z 的加密、分卷、固实压缩绕不开 7-Zip 官方那套源码。这份「C调用7z解压缩封装代码」就是干这个的——把 7-Zip 的 SDK 包一层 C 接口让你不用直接啃它那套 COM 风格的原始 API就能在自己的工程里调Extract、Compress这类操作。适合谁做安装包、做文件管理工具、做自动化部署脚本的 C 开发者尤其是那些需要在 Windows 上静默解压、又不想依赖外部 exe 进程的场景。下面我按「先跑通、再讲参数、最后说坑」的顺序拆一遍。2. 封装层拆解从 7z SDK 到可调用的 C 接口2.1 为什么不能直接调 7z.exe很多人第一反应是system(7z x archive.7z)或者CreateProcess起一个 7z.exe 子进程。能跑但问题一堆进程启动开销大批量解压几百个小包时性能直接崩拿不到细粒度进度只能靠解析 stdout 猜密码传递要走命令行参数有泄露风险最要命的是部署时得额外带一个 7z.exe用户环境里路径不对就翻车。封装层的价值在于把 7-Zip 的IInArchive、IOutArchive这些 COM 接口包成普通 C 类直接在你的进程空间里跑没有子进程开销进度回调、密码、分卷都能通过参数控制。常见做法是封装成一个SevenZipExtractor类构造时传压缩包路径Extract时传输出目录和回调函数。2.2 封装代码的核心结构先看头文件里暴露出来的接口长什么样这是理解整个封装思路的入口// SevenZipWrapper.h #pragma once #include string #include functional #include vector // 进度回调当前文件索引、总文件数、当前文件名 using ProgressCallback std::functionvoid(int, int, const std::wstring); class SevenZipWrapper { public: SevenZipWrapper(); ~SevenZipWrapper(); // 打开压缩包返回是否成功 bool Open(const std::wstring archivePath, const std::wstring password L); // 解压全部到指定目录 bool ExtractAll(const std::wstring outDir, ProgressCallback cb nullptr); // 只解压指定索引的文件 bool ExtractItems(const std::wstring outDir, const std::vectorint indices, ProgressCallback cb nullptr); // 获取包内文件列表 std::vectorstd::wstring GetFileList() const; // 获取错误信息 std::wstring GetLastError() const; private: struct Impl; Impl* pImpl; // Pimpl 惯用法隐藏 7z SDK 头文件 };这里用 Pimpl 惯用法是有意为之7-Zip SDK 的头文件又大又脏直接 include 到业务代码里会污染命名空间还可能和 Windows.h 冲突。把实现藏在.cpp里头文件只暴露干净的接口调用方不需要知道底下是 7z 还是别的库。2.3 打开压缩包Open 的实现逻辑Open这一步做三件事加载 7z 动态库、创建IInArchive对象、调用Open方法读取压缩包元信息。关键代码如下// SevenZipWrapper.cpp节选 #include SevenZipWrapper.h #include 7z/CPP/7zip/Archive/IArchive.h #include 7z/CPP/7zip/IPassword.h #include 7z/CPP/Common/MyCom.h struct SevenZipWrapper::Impl { CMyComPtrIInArchive archive; CMyComPtrISequentialInStream fileStream; std::wstring lastError; std::wstring password; }; bool SevenZipWrapper::Open(const std::wstring archivePath, const std::wstring password) { pImpl-password password; // 1. 创建文件流 CMyComPtrIInStream inStream; if (!CreateFileStream(archivePath.c_str(), inStream)) { pImpl-lastError L无法打开文件流; return false; } // 2. 根据文件头创建对应的解码器7z/zip/rar 等 if (!CreateArchiveObject(inStream, pImpl-archive)) { pImpl-lastError L不支持的压缩格式; return false; } // 3. 打开压缩包读取元数据 UInt64 scanSize 0; HRESULT hr pImpl-archive-Open( inStream, scanSize, nullptr // 密码回调稍后单独处理 ); if (hr SZ_ERROR_PASSWORD) { // 需要密码走密码回调 return OpenWithPassword(inStream); } if (hr ! SZ_OK) { pImpl-lastError L打开压缩包失败错误码: std::to_wstring(hr); return false; } return true; }逻辑说明CreateFileStream是封装的文件读取流7z SDK 要求实现ISequentialInStream接口这里用 Windows 的CreateFileReadFile包一层。CreateArchiveObject根据文件头判断是 7z 还是 zip分别创建NArchive::N7z::CHandler或NArchive::NZip::CHandler。Open的第三个参数是密码回调如果压缩包加密第一次调用会返回SZ_ERROR_PASSWORD这时需要重新用带密码回调的方式打开。参数说明scanSize是扫描到的数据大小一般传nullptr让 SDK 自己算。密码回调需要实现ICryptoGetTextPassword接口在CryptoGetTextPassword方法里返回pImpl-password。2.4 解压全部ExtractAll 的调用链ExtractAll的核心是构造IArchiveExtractCallback的实现类然后调archive-Extract// 解压回调实现简化版 class ExtractCallback : public IArchiveExtractCallback, public ICryptoGetTextPassword { public: ExtractCallback(const std::wstring outDir, ProgressCallback cb) : outDir_(outDir), progressCb_(cb), currentIndex_(0) {} // 7z SDK 回调准备解压某个文件 STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream** outStream, Int32 askExtractMode) override { currentIndex_ index; // 获取文件名 UString name GetProperty(index, kpidPath); std::wstring fullPath outDir_ L\\ name.Ptr(); // 创建输出文件流 outFileStream_ new COutFileStream; if (!outFileStream_-Create(fullPath.c_str())) { return E_ABORT; } *outStream outFileStream_; outFileStream_-AddRef(); // 触发进度回调 if (progressCb_) { progressCb_(index, totalFiles_, name.Ptr()); } return S_OK; } // 密码回调 STDMETHOD(CryptoGetTextPassword)(BSTR* password) override { *password SysAllocString(password_.c_str()); return S_OK; } private: std::wstring outDir_; ProgressCallback progressCb_; UInt32 currentIndex_; CMyComPtrCOutFileStream outFileStream_; }; bool SevenZipWrapper::ExtractAll(const std::wstring outDir, ProgressCallback cb) { ExtractCallback* callback new ExtractCallback(outDir, cb); CMyComPtrIArchiveExtractCallback extractCb callback; // 传 -1 表示解压全部 HRESULT hr pImpl-archive-Extract( nullptr, // 文件索引数组nullptr 配合 -1 表示全部 (UInt32)(Int32)-1, false, // testModefalse 表示真正解压 extractCb ); if (hr ! SZ_OK) { pImpl-lastError L解压失败错误码: std::to_wstring(hr); return false; } return true; }逻辑说明Extract的第二个参数是(UInt32)(Int32)-1这是 7z SDK 的约定表示解压所有文件。GetStream回调在解压每个文件前被调用在这里创建输出文件流并触发进度回调。askExtractMode参数区分是解压还是仅测试一般传NArchive::NExtract::NAskMode::kExtract。参数说明COutFileStream是 7z SDK 自带的文件输出流类直接用它比手写ISequentialOutStream省事。进度回调的totalFiles_需要在Open之后通过archive-GetNumberOfItems获取并传入回调对象。3. 编译与集成把 7z SDK 塞进你的工程3.1 源码目录结构7-Zip 官方源码包解压后和封装相关的目录主要是这几个目录内容是否必须CPP/7zip/Archive各格式的编解码器实现是CPP/7zip/Common公共工具类是CPP/Common基础类型和字符串处理是CPP/WindowsWindows 平台封装是C底层压缩算法是CPP/7zip/Bundles预置的编译配置参考用我一般只把CPP和C两个目录拷进工程Bundles里的Format7zF配置可以作为编译选项的参考但不需要整个目录。3.2 CMake 集成配置用 CMake 的话核心是把 7z 的源码编成静态库再链接到你的目标# CMakeLists.txt cmake_minimum_required(VERSION 3.15) project(SevenZipDemo CXX) set(CMAKE_CXX_STANDARD 17) # 7z 源码目录 set(SEVENZIP_DIR ${CMAKE_SOURCE_DIR}/third_party/7z) # 收集 7z 源文件 file(GLOB_RECURSE SEVENZIP_SOURCES ${SEVENZIP_DIR}/CPP/7zip/Archive/*.cpp ${SEVENZIP_DIR}/CPP/7zip/Common/*.cpp ${SEVENZIP_DIR}/CPP/Common/*.cpp ${SEVENZIP_DIR}/CPP/Windows/*.cpp ${SEVENZIP_DIR}/C/*.c ) # 排除不需要的格式减小体积 list(FILTER SEVENZIP_SOURCES EXCLUDE REGEX Archive/(Rar|Cab|Iso|Tar|GZip)) add_library(sevenzip STATIC ${SEVENZIP_SOURCES}) target_include_directories(sevenzip PUBLIC ${SEVENZIP_DIR}) target_compile_definitions(sevenzip PRIVATE UNICODE _UNICODE WIN32 _WINDOWS ) # 你的封装库 add_library(sevenzip_wrapper STATIC SevenZipWrapper.cpp) target_link_libraries(sevenzip_wrapper PUBLIC sevenzip) # 测试程序 add_executable(test_extract main.cpp) target_link_libraries(test_extract PRIVATE sevenzip_wrapper)逻辑说明file(GLOB_RECURSE)递归收集所有源文件然后用list(FILTER ... EXCLUDE)排除不需要的格式。如果你只需要 7z 和 zip把 Rar、Cab、Iso 这些排除掉能显著减小编译时间和二进制体积。target_compile_definitions里的UNICODE和_UNICODE必须加否则 7z SDK 里的字符串处理会出问题。参数说明SEVENZIP_DIR指向你放 7z 源码的目录。如果编译时报#include windows.h找不到检查是否装了 Windows SDK。如果报LNK2019未解析符号大概率是漏了某个.cpp文件用GLOB_RECURSE一般不会漏但排除规则写错会误伤。3.3 最小可运行示例编译通过后用这个main.cpp验证// main.cpp #include SevenZipWrapper.h #include iostream int main() { SevenZipWrapper extractor; // 打开压缩包 if (!extractor.Open(Ltest.7z, L123456)) { std::wcerr L打开失败: extractor.GetLastError() std::endl; return 1; } // 列出文件 auto files extractor.GetFileList(); std::wcout L包内文件数: files.size() std::endl; for (const auto f : files) { std::wcout L f std::endl; } // 解压到 output 目录 bool ok extractor.ExtractAll(L./output, [](int current, int total, const std::wstring name) { std::wcout L[ current L/ total L] name std::endl; }); if (!ok) { std::wcerr L解压失败: extractor.GetLastError() std::endl; return 1; } std::wcout L解压完成 std::endl; return 0; }逻辑说明先Open打开压缩包密码作为第二个参数传入。GetFileList返回包内所有文件的相对路径。ExtractAll的第二个参数是进度回调每解压一个文件触发一次。如果Open返回 false用GetLastError拿具体错误信息。参数说明Open的密码参数是std::wstring传空字符串表示无密码。ExtractAll的输出目录如果不存在COutFileStream::Create会失败需要提前用CreateDirectory创建。进度回调的current是从 0 开始的索引total是文件总数。4. 避坑与排查那些让我加班到凌晨的细节4.1 中文路径乱码现象压缩包路径或输出目录含中文时Open返回失败错误码SZ_ERROR_OPEN。原因7z SDK 内部用UStringUTF-16但如果你在CreateFileStream里用了std::string转std::wstring的窄字符转换中文会丢。常见错误是用std::wstring(path.begin(), path.end())这种粗暴转换。解决统一用std::wstring传路径Windows 下用MultiByteToWideChar(CP_UTF8, ...)做转换不要用ATL的CA2W除非确认编码是 ANSI。我一般会在封装层加一个Utf8ToWide工具函数所有外部传入的std::string路径先转 UTF-8 再转宽字符。4.2 分卷压缩包只解出第一卷现象.7z.001、.7z.002这种分卷包Open时只传了.001解压出来的文件不完整。原因7z SDK 的IInArchive::Open只接受单个流分卷需要自己实现IInStream的Seek和Read在跨卷边界时切换到下一个文件。解决封装一个MultiVolumeStream类内部维护一个文件句柄列表Read时如果当前卷读完自动打开下一个卷。文件名规律一般是.001、.002按序号递增。注意Seek要支持跨卷定位否则 SDK 读元数据时会出错。4.3 解压大文件时内存暴涨现象解压一个几 GB 的固实压缩包进程内存占用飙到几个 GB最后 OOM。原因7z 的固实压缩solid compression把多个文件压成一个块解压时需要把整个块读进内存。如果你的ISequentialOutStream实现是先把数据攒在内存再写文件内存直接翻倍。解决COutFileStream本身就是流式写文件的不要自己再包一层内存缓冲。如果必须用自定义输出流确保Write方法是直接写磁盘不要攒。另外可以在Extract时传askExtractMode kTest先测试确认没问题再真正解压。4.4 密码回调返回空导致死循环现象加密压缩包密码传错时程序卡死CPU 占用 100%。原因CryptoGetTextPassword返回S_OK但密码为空7z SDK 会反复调用密码回调形成死循环。解决在密码回调里判断如果密码为空或已尝试过返回E_ABORT而不是S_OK。我一般会在回调对象里加一个passwordTried_标志第一次返回密码第二次直接E_ABORT。4.5 编译时windows.h和 7z 头文件冲突现象编译报min/max宏冲突或者INT32重定义。原因windows.h默认定义min和max宏7z SDK 里用了std::min冲突。另外 7z 的Common/MyTypes.h和 Windows 的basetsd.h都可能定义INT32。解决在包含windows.h之前加#define NOMINMAX并且把 7z 头文件的 include 放在windows.h之后。如果还有INT32重定义在 7z 头文件前加#define _7ZIP_ST禁用单线程版本的类型定义。5. 进阶技巧批量解压与错误恢复的实战配置批量解压几百个压缩包时逐个OpenExtract的效率瓶颈不在解压本身而在反复创建和销毁IInArchive对象。我的做法是复用同一个SevenZipWrapper实例只重置内部状态// 批量解压复用实例 SevenZipWrapper extractor; std::vectorstd::wstring archives GetArchiveList(L./input); for (const auto arc : archives) { // 每次 Open 前先 Close 释放上一个包 extractor.Close(); if (!extractor.Open(arc, L)) { // 记录失败继续下一个 LogError(arc, extractor.GetLastError()); continue; } std::wstring outDir L./output/ GetFileNameWithoutExt(arc); CreateDirectoryW(outDir.c_str(), nullptr); if (!extractor.ExtractAll(outDir)) { LogError(arc, extractor.GetLastError()); // 解压失败时清理半成品目录 RemoveDirectoryRecursive(outDir); } }逻辑说明Close方法需要释放IInArchive和文件流但保留SevenZipWrapper对象本身。这样避免了反复加载 7z 动态库的开销。CreateDirectoryW在解压前创建输出目录因为COutFileStream::Create不会自动创建父目录。参数说明GetFileNameWithoutExt需要自己实现注意处理.7z.001这种双扩展名的情况。RemoveDirectoryRecursive在解压失败时清理避免残留半成品文件干扰下次运行。错误恢复方面我一般会加一个重试机制第一次解压失败后用kTest模式先测试压缩包完整性如果测试通过再重试解压测试失败直接标记为损坏包跳过。这样能把「密码错误」和「文件损坏」区分开避免在损坏包上浪费重试次数。还有一个容易被忽略的点解压后的文件权限。7z 包内可能包含只读文件COutFileStream创建的文件默认继承父目录权限但如果包内文件属性是只读解压后需要手动SetFileAttributesW清除只读属性否则后续删除目录会失败。我一般会在ExtractAll完成后遍历输出目录把所有文件的只读属性去掉。从那以后我每次集成新的压缩库都会先拿一个带密码、带中文路径、带分卷的「三合一」测试包跑一遍确认这三个场景都过了再往业务代码里塞。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Matlab相机标定例程去畸变全解析:原理、代码与工程实践

Matlab相机标定例程去畸变全解析:原理、代码与工程实践

1. 进入标定工程前,先把“去畸变”这件事想清楚做视觉测量、三维重建或者机器人抓取的工程师,多半都跟相机标定打过交道。Matlab的相机标定例程在业界用得相当广,因为它有图形化界面、能直接导出参数、还自带批量处理工具。但真正到了项目落地…

2026/10/14 12:22:30 阅读更多 →
Python库生态与选型实践指南

Python库生态与选型实践指南

1. Python库生态全景解析 Python作为当下最流行的通用编程语言,其强大之处不仅在于简洁的语法,更在于丰富的库生态系统。根据官方PyPI仓库统计,截至2023年Python可用库数量已超过45万个,这个数字每天都在增长。面对如此庞大的资源…

2026/10/14 12:21:28 阅读更多 →
焊接机器人视觉引导:三维点云配准与强化学习轨迹规划实战

焊接机器人视觉引导:三维点云配准与强化学习轨迹规划实战

简介:围绕工业视觉引导焊接这一智能制造关键场景,这份PDF文档系统讲解如何基于PyTorch完成三维点云配准与强化学习相结合的机器人轨迹规划,面向机器人视觉、智能制造和深度学习方向的研究生、算法工程师及项目开发者。内容共30页,…

2026/10/14 12:21:28 阅读更多 →

最新新闻

Sybase多行转一行实战:临时表+自连接实现行转列

Sybase多行转一行实战:临时表+自连接实现行转列

1. 项目概述:Sybase中多行转一行的实战需求与本质理解在某金融系统报表模块的日常维护中,我接手了一个典型的“宽表生成”需求:原始交易明细表里,同一笔订单号(order_id)可能对应多条商品记录,每…

2026/10/14 15:35:29 阅读更多 →
QDMA中断机制全解析:从硬件配置到驱动调试实战

QDMA中断机制全解析:从硬件配置到驱动调试实战

做FPGA加速卡的人,很少有人没跟QDMA中断打过交道。行列传输的带宽指标很容易刷得漂亮,但真正让整个系统稳定跑起来的,往往是中断这条“最后一公里”。QDMA(Queue Direct Memory Access)这类基于队列的DMA引擎&#xff…

2026/10/14 15:35:29 阅读更多 →
Python招聘数据爬虫到可视化全流程实战解析

Python招聘数据爬虫到可视化全流程实战解析

简介:基于Python实现的北京市大数据岗位招聘数据分析与可视化项目,面向计算机、通信、人工智能、自动化等专业学生及数据分析从业者,既可作为毕业设计与期末大作业的完整参考,也适合Python初学者借助真实场景掌握爬虫、数据处理和…

2026/10/14 15:35:29 阅读更多 →
Kubernetes Secret可选性真相:不是Secret设optional,而是Pod用Secret时设

Kubernetes Secret可选性真相:不是Secret设optional,而是Pod用Secret时设

1. Kubernetes Secret 的“可选性”本质:它根本不是字段,而是使用方式很多人第一次看到这个标题时会下意识去翻 Kubernetes 官方文档的Secret对象定义,试图在spec或data字段里找一个叫optional: true的开关。我当年也是这么干的——花了整整一…

2026/10/14 15:35:29 阅读更多 →
Java Web考勤系统开发实战:SSM+JSP+layui+MySQL详解

Java Web考勤系统开发实战:SSM+JSP+layui+MySQL详解

简介:这是一套基于SSMJSPlayuiMysql的学生课堂考勤管理系统完整源码包,面向JavaWeb初学者及毕业设计者,覆盖辅导员、管理员、教师、学生四种角色,包含辅导员的学生管理、请假审批、签到列表,管理员的班级/辅导员/教师管…

2026/10/14 15:35:29 阅读更多 →
2026跨境电商新规落地后,这5个隐藏坑千万别踩,有人已亏光全年利润

2026跨境电商新规落地后,这5个隐藏坑千万别踩,有人已亏光全年利润

新规实施后的第一个完整季度,不少卖家发现账面利润和预期出现了明显偏差。有人归因于流量成本,有人怀疑汇率波动,但真正的问题往往藏在合规细节里。以下五个容易被忽略的环节,正在悄悄侵蚀利润空间。 一、低申报的“惯性操作”已进…

2026/10/14 15:34:27 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/14 7:53:34 阅读更多 →