
一张打不开的图纸逼我撸了一个 C 图纸数据提取工具libdxfrw 实战手记【免费下载链接】libdxfrwC library to read and write DXF/DWG files项目地址: https://gitcode.com/gh_mirrors/li/libdxfrw周四晚上十一点甲方发来一个.dwg附件麻烦明天一早把图上所有门洞尺寸统计出来。你双击打开AutoCAD 提示版本太新找在线转换工具文件传到一半弹窗要付费你甚至试了用文本编辑器强开 DXF——满屏的10,20,40代码组让人头皮发麻。如果你也被这种场景折磨过那么这篇关于 C 读写 DXF 与解析 DWG 的文章就是为你准备的。为什么这件事不该靠装个软件解决图纸数据的价值从来不在能看见而在能算。你要的不是打开这张图而是从里面把几何信息变成自己的数据——门洞尺寸进 Excel、圆心坐标进数据库、图框名做批量归档。手工操作一次两次还行几十张图纸就彻底失控了。所以正确姿势是写一个程序用 libdxfrw 把图纸解析成结构化数据。这是一个纯 C 的 CAD 文件解析库DXFASCII 与二进制读写通吃DWG 支持从 R14 一路读到 2015 版本跨平台GPL v2 开源。它不渲染、不编辑只做一件事把图纸文件翻译成你的程序能懂的对象。对工程软件开发者来说这恰恰是最难啃、也最值得借力的部分。第一次直面回调式解析先学会当观众libdxfrw 的读取模型很特别——它不主动给你数据而是叫你实现一个接口去接数据。这就像一场广播库里每读到一个实体就喊一嗓子来了一条线你得预先架好收音机。这个收音机就是继承自DRW_Interface的类。注意一个坑接口里全是纯虚函数你必须把所有回调都实现一遍哪怕大多数是空函数否则根本编译不过。这正是很多新手第一道坎。#include libdxfrw.h class DoorCollector : public DRW_Interface { public: void addHeader(const DRW_Header*) override {} void addLType(const DRW_LType) override {} void addLayer(const DRW_Layer) override {} void addDimStyle(const DRW_Dimstyle) override {} void addVport(const DRW_Vport) override {} void addTextStyle(const DRW_Textstyle) override {} void addAppId(const DRW_AppId) override {} void addBlock(const DRW_Block) override {} void setBlock(const int) override {} void endBlock() override {} void addPoint(const DRW_Point) override {} void addRay(const DRW_Ray) override {} void addXline(const DRW_Xline) override {} void addArc(const DRW_Arc) override {} void addEllipse(const DRW_Ellipse) override {} void addLWPolyline(const DRW_LWPolyline) override {} void addPolyline(const DRW_Polyline) override {} void addSpline(const DRW_Spline*) override {} void addKnot(const DRW_Entity) override {} void addInsert(const DRW_Insert) override {} void addTrace(const DRW_Trace) override {} void add3dFace(const DRW_3Dface) override {} void addSolid(const DRW_Solid) override {} void addMText(const DRW_MText) override {} void addText(const DRW_Text) override {} void addDimAlign(const DRW_DimAligned*) override {} void addDimLinear(const DRW_DimLinear*) override {} void addDimRadial(const DRW_DimRadial*) override {} void addDimDiametric(const DRW_DimDiametric*) override {} void addDimAngular(const DRW_DimAngular*) override {} void addDimAngular3P(const DRW_DimAngular3p*) override {} void addDimOrdinate(const DRW_DimOrdinate*) override {} void addLeader(const DRW_Leader*) override {} void addHatch(const DRW_Hatch*) override {} void addViewport(const DRW_Viewport) override {} void addImage(const DRW_Image*) override {} void addImageDef(const DRW_ImageDef*) override {} void addComment(const char*) override {} // 只关心直线和圆 void addLine(const DRW_Line l) override { printf(LINE: (%.2f,%.2f) - (%.2f,%.2f)\n, l.basePoint.x, l.basePoint.y, l.secPoint.x, l.secPoint.y); } void addCircle(const DRW_Circle c) override { printf(CIRCLE: center(%.2f,%.2f) r%.2f\n, c.basePoint.x, c.basePoint.y, c.radious); } };代码虽长但逻辑极简事件来了你只处理感兴趣的。DRW_Line继承自DRW_PointbasePoint是起点、secPoint是终点DRW_Circle的圆心也在basePoint半径字段叫radious——注意是radious不是radius这个拼写雷区曾让我对着编译错误懵了十分钟。最小可行路径30 行代码把 DXF 读干净有了接口读取就只剩三步建dxfRW对象、调用read、等回调被触发。int main(int argc, char* argv[]) { if (argc 2) { printf(usage: %s drawing.dxf\n, argv[0]); return 1; } DoorCollector collector; dxfRW dxf(argv[1]); dxf.setDebug(DRW::DEBUG); // 出问题时打开能输出解析详情 if (!dxf.read(collector, false)) { printf(读取失败检查版本或文件完整性\n); return 1; } return 0; }read的第二个参数是extextrusion含义是是否把 3D 拉伸转成 2D 平面坐标。只关心平面几何就传false简单直接。编译链接时把源码目录里的src/一起编进去即可。Windows 用户打开仓库自带的vs2013/libdxfrw.sln直接生成Linux 下推荐 CMakemkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease cmake --build . --config Release --target install顺带一提如果你拿到了库源码仓库里还带了dwg2dxf和dwg2text两个命令行工具它们本身就是DRW_Interface的最佳实现范例——看不懂接口怎么用就去读dwg2dxf/dx_iface.cpp那是活教材。写入是读取的镜像给程序画图的能力写入比读取更轻松因为不用实现接口直接调用dxfRW的writeXxx系列方法。想象你在用代码当绘图员定义一根线、画一个圆然后落笔成文件。void makeFixtureDXF(const char* path) { dxfRW dxf(path); dxf.setVersion(DRW::AC1015); // 输出为 ACAD 2000 格式兼容性最好 dxf.writeHeader(); DRW_Line line; line.basePoint {0.0, 0.0, 0.0}; line.secPoint {800.0, 0.0, 0.0}; // 一道墙的轮廓 DRW_Circle circle; circle.basePoint {400.0, 300.0, 0.0}; circle.radious 150.0; // 一个螺栓孔位 dxf.writeLine(line); dxf.writeCircle(circle); }注意细节写入前要调用writeHeader()打底否则文件头残缺很多 CAD 软件打开会报文件损坏。setVersion里DRW::Version枚举从AC1009R11/R12一路到AC1032ACAD 2018输出低版本格式能兼容老软件但会丢失新版本特有的对象——这是图纸交换里的经典取舍。解析 DWG一群方言各异的翻译官如果说 DXF 是明文电报DWG 就是加了密的方言录音——二进制、压缩、版本间字段对不上。这解释了一个反直觉的现象为什么同一段代码读 DXF 顺风顺水读 DWG 却老出错因为 DWG 没有统一格式每个 AutoCAD 大版本都改过内部布局。libdxfrw 的应对是给每种版本配一个专属翻译官源码在src/intern/里一目了然dwgreader15.cpp—— R14 的翻译官dwgreader18.cpp—— R2004 的翻译官dwgreader21.cpp—— R2007 的翻译官dwgreader24.cpp—— R2010 的翻译官dwgreader27.cpp—— R2013/R2015 的翻译官版本越老语法越简单解析越稳版本越新翻译官越吃力。所以遇到高版本 DWG比如 2018 以后存的最省事的招数是先请 AutoCAD 或批量工具把它降级另存成 2004 版 DXF再用 libdxfrw 读取——别硬刚格式格式赢不了。读取 DWG 的代码和 DXF 几乎一样只是入口类不同#include libdwgr.h dwgR dwg(drawing.dwg); if (dwg.read(collector, false)) { printf(DWG 解析完成\n); }它复用的还是同一套DRW_Interface回调。这正是这个库设计的聪明之处上层接口不变底层翻译官随便换。三个坑我都替你踩过了坑一中文全变问号。我曾处理一批含中文标注的图纸文本读出来全是???。后来发现 DXF 文件头里有$DWGCODEPAGE声明简体中文工程图通常声明ANSI_936GB2312或UTF-8。库内部有DRW_TextCodec负责代码页转换支持 CP932/936/949/950 等但转换依赖文件头声明正确。如果你的图纸来源杂乱读取前先确认文件头里的代码页声明必要时手工补上否则文本类实体直接乱码。坑二实体凭空消失。第一次跑程序统计出来的图元数比图纸上肉眼可见的少了四成。排查后发现大量门、窗、家具画在块Block里而块的引用是INSERT实体。libdxfrw 不会替你展开块——它只把addInsert和块定义回调给你展开嵌套块的几何变换得自己算。这不是 bug是设计块内实体带坐标变换展开逻辑因场景而异库选择把决定权留给你。坑三大文件卡到怀疑人生。一张 50MB 的市政管网图单线程顺序解析要二十多秒。优化思路很简单回调里只做轻量数据收集把耗时处理统计、入库、写 Excel挪到解析完成之后批量做同时开-O2编译优化。如果还不够把多张图纸按文件分片丢进线程池并行解析——反正每张图纸的解析彼此独立天然可并行。libdxfrw 解析 DWG 流程图闭环把门洞尺寸统计工具做出来回到开头的故事。把前面的知识点串起来一个能用的工具长这样输入命令行传入图纸路径DWG 或 DXF 自动识别回调在addLine/addArc/addInsert里筛出门洞相关图层记录起点、终点、角度后处理把弧线段拟合成门洞弧计算净宽输出 CSV批处理遍历目录下所有图纸文件每张图一个线程。第二天早上你交给甲方的不是一张截图而是一份自动生成的尺寸统计表。从那以后每次收到帮我统计一下的需求你只需要更新脚本跑一遍。这就是把图纸变成数据的威力。下一步往哪挖这个库的边界很清楚它管解析不管业务。想更进一步可以看这几个方向深度解析对象层drw_objects.cpp里的图层、线型、标注样式对象做图纸规范检查时用得上块展开算法写一个applyInsertTransform的通用变换工具是你超越回调搬运工的关键一跃从库到应用把收集到的实体数据喂给渲染引擎OpenGL/Qt你的轻量图纸查看器就有了雏形。源码在手边仓库地址https://gitcode.com/gh_mirrors/li/libdxfrw含完整示例和dwg2dxf工具头文件注释写得相当诚恳src/drw_interface.h里每个回调都标了触发时机。10 分钟的阅读往往能省下你一下午的试错。CAD 文件格式是一座迷宫但好消息是——你不用亲自走遍每一条走廊libdxfrw 已经把地图画好了。【免费下载链接】libdxfrwC library to read and write DXF/DWG files项目地址: https://gitcode.com/gh_mirrors/li/libdxfrw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考