简介面向自然资源领域GIS数据处理人员这款批量标准矢量shp互转txt工具可高效完成Shapefile与文本格式间的转换适用于建设用地报批、设施农用地上图、卫片处理等场景解压后直接运行主程序即可使用无需复杂安装。压缩包共含147个文件除可执行程序与配置文件外还囊括dll动态链接库、坐标系统参数csv、投影定义wkt、矢量样例及日志说明等整体大小仅17.12MB兼容Windows 7/10等主流系统。工具的核心在于读取SHP中的几何与属性信息输出结构化TXT数据同时内置多种坐标参考支持方便在非GIS环境中进行后续统计与规划分析。包内文件类型覆盖了从坐标基准、投影定义到格式转换所需的完整环节适合需批量处理标准矢量数据并保留坐标精度的从业者直接取用。目前已有4852人学习使用是一份成熟、轻量且即开即用的实用工具。1. 批量SHP转TXT先别急着改后缀先想清楚坐标和字段往哪放接到一批“标准矢量shp”数据对方系统只认txt点名要SHP互转TXT。我的第一反应不是去搜转换器而是先问了一句转出来的txt拿给谁读、读完之后还回不回shp。因为SHP和TXT根本不是同一层东西一个二进制带索引一个纯文本裸数据强行改后缀换来的只是一堆乱码。这个工具能解决的是把shp里的几何坐标、属性字段、投影信息按一套固定约定写成txt也能把这种txt重新拼回shp。适合手里有大量分幅shp、要给老系统喂数据、或者要在不同团队之间用文本过一遍数据的从业者。先把格式约定想明白后面批量转才不翻车。2. SHP与TXT的本质差异一个图层拆成五个文件一个文本全靠约定2.1 SHP不是一个文件主文件、索引、属性、投影各司其职很多刚接触矢量数据的人以为SHP就是一个.shp后缀的文件其实一个完整的Shapefile是“一组文件”缺一个都会出问题。主文件.shp里按顺序存每条要素的几何坐标从几点到几点、哪个是外环哪个是内环全部写在这个二进制文件里。.shx是空间索引帮GIS软件快速定位某条要素没有它桌面软件很可能直接提示图层打不开。.dbf是属性表字段名、字段类型、字段长度都定义在它的头部点线面的属性值就存在这个dBase格式文件里。.prj用WKT文本描述坐标系告诉软件这份数据在哪个投影下。.cpg则声明字符集中文属性不乱码全靠它。我做数据边检查时习惯先看这四个伴生文件是否齐全再决定要不要碰几何。缺.prj是常态因为有些平台导shp时不写投影缺.cpg更常见属性一打开就是乱码缺.shx最麻烦图层直接读不出来。把SHP转成TXT之前得先把这些信息捞出来否则转出来的文本既不带投影也不带编码说明下游拿到就是一堆裸数字。文件后缀存储内容缺失后的典型表现.shp要素几何结构坐标序列、环序直接打不开或要素显示为空.shx要素位置索引部分软件拒绝加载.dbf属性字段与属性值几何还在属性表全丢.prj坐标系WKT信息图层能画但坐标对不上实景.cpg属性字符集声明中文属性乱码2.2 TXT的两种组织模式行式适合入库块式适合人读TXT本身没有固定结构所谓“标准txt”其实是转换双方约定出来的。实际项目里最常见的两种组织模式一种是行式一行对应一个坐标点另外带FID、环号、顺序号这些辅助列另一种是块式把一条要素的所有坐标写在连续段落里要素之间用空行或标记分隔。行式的优点是对数据库友好PG、Oracle、甚至Excel都能直接导入逐行解析也简单缺点是一条线或一个面被拆成多行读取时要按FID和SEQ重新组装。块式更适合人眼检查一条要素就是一个整体但脚本解析时反而要处理段落边界做批量统计不如行式顺手。我一般默认输出行式因为下游接数大概率是入库块式留给那种需要人工翻阅的场景。这条选择直接决定互转脚本的复杂程度。行式TXT需要一个明确的排序规则先用FID区分要素再用SEQ区分点的先后顺序线和面还要用RING区分第几个路径或环。这三列就是整个TXT的骨架少了任何一列逆转换时几何就会乱套。2.3 互转工具的真正价值把“解析”变成“约定”SHP的二进制结构是公开固定的GDAL/OGR这类库解析起来没有任何难度。真正的难点在于TXT的结构是自定义的。工具的作用不是“把文件换个格式”而是把SHP里读出的每一条几何和属性按事先约定好的文本结构写出去反向转换时再按同一套结构逐行读回来。所以这个工具的核心资产不是代码量而是这套字段映射约定。约定定了SHP转TXT和TXT转SHP就是同一件事的两条路约定不清晰今天转出来一个样明天再加个字段又是另一个样日志和排错全得重来。3. 设计可逆的文本结构字段映射、几何编码与坐标精度怎么定3.1 一套行列混合的TXT约定几何行、属性行、元信息分离我的做法是把TXT设计成三种行头部元信息行、几何坐标行、属性行。头部行用来记录坐标系、分隔符、版本信息防止下游拿到的txt变成“裸坐标”。几何行每行只放一个点和它的组织信息属性行每行放一条要素的完整属性用FID关联。#HEADER|EPSG4326|SEP|ENCODINGUTF-8 #ATTR|FID|NAME|TYPE|AREA #ATTR|0|地块A|POLYGON|125.36 #ATTR|1|地块B|POLYGON|89.12 #GEOM|FID|RING|SEQ|X|Y #GEOM|0|0|0|120.15234|30.23456 #GEOM|0|0|1|120.15789|30.23891 #GEOM|0|1|0|120.16012|30.23001 #END这个结构的核心逻辑是属性和几何分开存避免一条多点的面要素把属性值重复几百遍。FID是要素编号几何行和属性行靠它关联。RING用来区分面要素的外环和内孔外环记为0内孔从1开始编号。SEQ是点在该环内的顺序号逆转换时先按FID分组再按RING和SEQ排序就能还原坐标序列。参数上分隔符用|而不是逗号是因为属性值里出现逗号的概率远大于竖线。EPSG4326写进头部而不是单独文件是为了让txt自解释反向转shp时脚本读这个值直接生成对应的.prj文件。3.2 几何类型转换规则点、线、面的RING与SEQ约定文本化几何时最容易错的是面的内环。SHP的面由外环和若干内环组成外环逆时针、内环顺时针是常见约定。转TXT时如果只按顺序写坐标不标记环号逆转换时大概率会把孔洞当成独立环或者直接丢掉。几何类型RING取值SEQ规则备注点固定0单点要素固定为0坐标占一行线0开始递增每个RING对应一条路径多段线按路径拆面0为外环1开始为内环每个环内独立排序内外环必须分开标记坐标精度也是互转参数里影响最大的一项。SHP内部存储的是双精度浮点TXT如果直接按python的默认浮点输出一个小数位都不丢文件体积翻倍下游解析还慢如果四舍五入到6位小数经纬度下误差约0.1米投影坐标下误差约0.1个地图单位。我的经验是投影坐标保留3位小数经纬度保留7位小数批量转换前统一设一个PRECISION参数不要每份文件单独手动改。多线要素在SHP中表示为多个路径每条路径各自有起点终点。转TXT时每条路径单独分配一个RING编号SEQ从0重新开始。这样写的好处是逆转换时不需要额外判断路径边界直接按RING聚合就行。3.3 属性字段映射字段名截断、类型归一化与顺序固定dbf属性表里字段名最长的历史限制是10个字节中文名在GBK编码下只能存3个字转换时经常遇到字段名被截断或者直接丢失。我的做法是转换前先做一张字段映射表把源字段名映射成英文短名TXT头部行记录映射关系反向转回shp时再换回来。field_map { NAME: 地块名称, TYPE: 用地类型, AREA: 面积, }这段映射表写在转换脚本里只是一个dict但它是整个批量流程里最容易被忽略的维护点。新数据来了先看字段名是否一致不一致就补映射不要直接跑脚本。源数据字段类型也不可全信面积字段在dbf里可能是浮点在txt里统一按字符串输出逆转换时按映射表里的类型转换回去。3.4 从TXT恢复SHP分组、排序、拼几何一步都不能乱TXT转SHP是前面过程的逆操作。解析时不能逐行直接写入必须先按FID分组再按RING、SEQ排序最后才生成几何。这个顺序一旦乱了点的连线就会来回跳面要素直接变成一个自相交的怪物。def rebuild_geometry(rows): grouped {} for row in rows: fid int(row[FID]) grouped.setdefault(fid, []).append(row) for fid, items in grouped.items(): items.sort(keylambda r: (int(r[RING]), int(r[SEQ]))) points [(float(r[X]), float(r[Y])) for r in items] yield fid, points代码逻辑不复杂但注意先分组再排序这条顺序。如果先排序再分组多个要素的SEQ会交叉排序结果完全错乱。函数里返回一个生成器每处理完一个FID就产出几何适合大文件流式处理避免几万条要素一次性挤进内存。面要素恢复时还要额外检查首尾坐标是否闭合SHP格式里外环的最后一个点和第一个点相同。如果TXT里没有存闭合点逆转换前要自动补上否则生成的shp在GIS里会提示几何错误。4. 批量转换的落地实现脚本骨架与常见问题排查4.1 单文件转换流程先用ogr2ogr做中间态再排版成TXT直接解析SHP二进制再写TXT不是不行只是要同时处理.shp、.dbf、.prj三个文件的读写代码量大且容易在环序和编码上出错。我习惯用ogr2ogr把SHP转成GeoJSON作为中间态再在Python里读取GeoJSON并排版成目标TXT这样投影信息被保留在JSON的crs字段里几何结构也被统一成GeoJSON的坐标数组。ogr2ogr -f GeoJSON /tmp/intermediate.json 输入.shp -t_srs EPSG:4326 -lco COORDINATE_PRECISION7参数说明-t_srs EPSG:4326表示把坐标转换到WGS84经纬度如果源数据本身就是投影坐标且下游要求保留原始坐标系这个参数可以去掉COORDINATE_PRECISION7控制输出坐标小数位设置太高会让中间文件膨胀设置太低会导致几何偏移。中间文件用/tmp目录是为了避免和源数据混在一起批量跑完直接清理。4.2 批量遍历与日志输出失败时要能定位到具体文件批量转换的第一步永远是“遍历目录”但不是简单glob一下shp就完事。源shp旁边要有.shx和.dbf否则转换过程会隐式报错。批量脚本里我会同时检查这几个伴生文件是否存在缺失的文件单独记录到日志而不是让整个任务中断。from pathlib import Path import subprocess, json, logging def batch_convert(src_dir, dst_dir): src_dir Path(src_dir) dst_dir Path(dst_dir) dst_dir.mkdir(parentsTrue, exist_okTrue) for shp in src_dir.glob(*.shp): if not (shp.with_suffix(.shx).exists() and shp.with_suffix(.dbf).exists()): logging.error(f{shp.name} 缺少伴生文件跳过) continue txt_path dst_dir / (shp.stem .txt) try: convert_single(shp, txt_path) logging.info(f{shp.name} - {txt_path.name} OK) except Exception as e: logging.error(f{shp.name} 转换失败: {str(e)})代码里log记录了三类信息跳过原因、成功文件、失败文件。这样跑完一批几百个文件直接看日志就能定位是哪几个文件出了问题不需要从头再跑一遍。continue只是跳过当前文件不会中断整个批量任务这在处理几十个G的数据集时尤其重要。4.3 单文件转换函数读GeoJSON、写TXT的完整骨架def convert_single(shp_path, txt_path): tmp_json /tmp/{}.json.format(shp_path.stem) subprocess.run([ ogr2ogr, -f, GeoJSON, tmp_json, str(shp_path), -t_srs, EPSG:4326, -lco, COORDINATE_PRECISION7 ], checkTrue) with open(tmp_json, encodingutf-8) as f: data json.load(f) with open(txt_path, w, encodingutf-8) as out: out.write(#HEADER|EPSG4326|SEP|ENCODINGUTF-8\n) for feature in data[features]: fid feature[properties].pop(FID, 0) out.write(#ATTR|{}|{}|{}\n.format( fid, feature[geometry][type], |.join(str(v) for v in feature[properties].values()) )) coords feature[geometry][coordinates] write_geometry_rows(out, fid, coords) out.write(#END\n)参数说明feature[properties].pop(FID, 0)把FID从属性里摘出来单独写不参与后续属性值拼装write_geometry_rows根据geometry type判断是Point、LineString还是Polygon分别按前面约定的RING/SEQ规则输出。写属性时统一用字符串因为txt本身不分类型类型信息在头部字段映射表里记录逆转换时再恢复。这里有个常见坑GeoJSON的Polygon坐标是四层嵌套的最外层是环列表转TXT时一定要把每层循环拆清楚。面要素的coordinates形如[[[x1,y1],[x2,y2],...], [内环...]]外层enumerate得到的就是RING编号。4.4 常见问题排查五条高频踩坑记录踩坑一转出来的TXT用文本编辑器打开是乱码。现象属性里中文全部变成“锟斤拷”或者干脆问号。原因源SHP的.dbf是GBK编码脚本直接按UTF-8读。解决读取.cpg文件判断原始编码没有.cpg就按GBK尝试转成txt时统一用UTF-8并带上BOM让记事本也能正确识别。踩坑二面要素逆转换回SHP后变成一个自相交的乱环。现象GIS里检查几何报错悬空节点和自相交一堆。原因TXT里的SEQ顺序被Excel排序打乱逆转换时没有重新排序。解决逆转换脚本里先按FID分组再按RING和SEQ排序不要依赖文件本身的物理行序。Excel打开TXT再另存会重排行序这个操作会悄悄破坏几何。踩坑三转回SHP后跟其他图层套不上位置整体偏移。现象点在一个位置底图在另一个位置差几公里到几十公里。原因SHP转TXT时用-t_srs EPSG:4326做了投影转换但TXT头部没记录转换前坐标系逆转换时按脱带坐标直接建.shp当然对不上。解决TXT头部除了EPSG编号还要留一个SRC_EPSG字段记录源shp的原始坐标系逆转换时按头部信息恢复原始投影。踩坑四批量跑到一半失败不知道哪些文件已经转完。现象日志只写了“batch error”没明细。原因脚本没有对单文件做异常捕获一个坏文件导致整个循环中断。解决每个文件单独包try/except外层的loop继续往下跑日志里记录文件名和错误信息。已成功的文件不重复转换靠输出目录里同名txt是否存在做判断即可。踩坑五属性值里的分隔符把列弄串了。现象一行的属性列数比其他行多解析时错位。原因属性值里包含了分隔符|输出时没有转义。解决输出属性值前把|替换成#124;之类占位符解析时再替换回来同时头部声明转义规则让下游知道这不是笔误。5. 进阶验证转完不等于交付先做三件事再发数据转换脚本能跑通只是第一步。我后来在交付前都会强制走三遍校验三遍全过了才敢把txt发出去。第一遍是要素数量核对用ogrinfo统计源shp要素条数再去TXT里数#ATTR行两个数字对不上说明转换过程中丢要素了。第二遍是坐标抽检随机挑三五条要素把TXT里的前几个坐标点拿去和源shp在GIS里对比如果偏移超过预设的精度范围基本可以断定投影或精度参数有问题。第三遍是逆转换验证把生成好的TXT再转回SHP转完的图层和原图叠加看形状是否完全重合。def check_feature_count(shp_path, txt_path): import subprocess result subprocess.run( [ogrinfo, -so, -q, shp_path], capture_outputTrue, textTrue ) shp_count result.stdout.split()[-1].strip() with open(txt_path, encodingutf-8) as f: txt_count sum(1 for line in f if line.startswith(#ATTR)) if shp_count txt_count: print(f{txt_path} 要素数一致{shp_count}) else: print(f数量不一致shp{shp_count}, txt{txt_count})这套校验代码逻辑很直白ogrinfo -so只输出概要不加载全部几何秒级完成txt按行统计#ATTR效率同样高。每批几百个文件三遍校验跑完也就十几分钟比盲目交付之后被对方打回重做省时间得多。从那以后我每次批量转换完都会习惯性先看一眼日志尾部再跑一遍要素数校验脚本确认无误才发出去。这套流程不复杂但能拦住绝大部分低级错误希望帮到你。本文还有配套的精品资源点击获取