
简介面向GIS数据处理人员这套ArcGIS工具箱提供shp矢量数据与txt文本之间的双向转换能力尤其针对“占补平衡”类土地数据整理场景可帮助用户快速提取几何与属性字段并保持面积总量一致。资源包共11个文件总大小仅317KB主要包括ArcGIS自定义工具箱.tbx、Python转换脚本.py、使用说明与WKID坐标参考列表.docx另含少量工程配置文件.xml、.iml等便于用户理解工具结构并按需调整。使用者可依据配套使用说明加载工具箱或直接运行脚本结合WKID坐标参考列表核对空间参考有效规避坐标系统不匹配问题熟悉Python的开发者还能基于源脚本进一步定制转换规则适配批量任务或特定字段映射需求。目前已有1676人学习下载适合在国土调查、数据入库、跨平台数据交换等场景中快速完成文本化处理。1. 项目概述与需求背景1.1 为什么要做“shp转txt”这个小工具在GIS行业待久了你会发现一个特别常见的需求拿着一个shp文件想把它的属性表导出来给非GIS的人看或者丢给下游的程序做数据接入。ArcGIS Desktop本身有“打开属性表-导出”的功能但导出来的要么是dbf要么是Excel要么是xml。真有合作方开口就是要txt的时候往往意味着他们那边有一套老的业务系统只认这种最朴素的文本格式。我刚接触这个需求时也愣了一下ArcGIS里不是有“Table to Text”工具吗直接用不就行了。后来在实际项目里跑了一圈发现这个内置工具导出的txt带了一堆表头描述和字段元信息行数一多文件就变得臃肿而且编码上经常出问题拿给开发那边解析的时候人家直摇头。于是我自己花了一个晚上基于ArcGIS的Python环境写了一个自定义工具箱来专职干这件事把shp的属性表干净利落地转成一行一条记录的txt。这个工具适合谁第一类是自然资源、测绘、城市规划领域的从业者经常需要把成果数据整理成甲方要求的交换格式第二类是搞数据接驳的技术人员拿shp当过河拆桥的中间格式下游只要纯文本第三类是刚学ArcPy想写自己的工具箱的入门者这个案例麻雀虽小五脏俱全——参数定义、游标遍历、编码处理、批处理都覆盖了。1.2 方案选型为什么用ArcGIS自定义工具箱而不是手动复制粘贴先说说我踩过的弯路。最早一批数据我是人工处理的打开属性表、全选、复制贴到Excel里再另存为txt。当时只有几十条记录还能接受。后来项目一换数据量直接到了二十万条记录属性字段十几个我再复制粘贴不仅手酸而且Excel在复制大字段时会截断2000多个字符以后的内容数据真真实实出了错。那一次返工让我下定决心——必须写成工具。市面上的选择其实有几个GDAL/OGR命令行转换、Python的shapefile库pyshp、ArcGIS自带的Table to Text工具。我最终选了ArcGIS环境下的自定义工具箱原因有三条一是环境天然。做ArcGIS项目的人电脑上必然有ArcGIS Desktop或者ArcGIS Pro不需要额外安装Python库。二是参数界面友好。自定义工具箱可以把图层、字段、编码方式、分隔符都暴露成可视化面板项目组的实习生都能直接用。三是保真度高。用ArcPy的SearchCursor游标读取属性值时ArcGIS会帮我们处理字段类型转换拿到的数据跟你在ArcMap里看到的一模一样这一点是很多第三方库做不到的有些第三方库对日期字段的解析会让你抓狂。2. 核心细节解析与实操要点2.1 字段处理OID、几何和字段类型深度还原写这个工具最核心的一个环节就是确定要导出哪些字段。作者默认情况下shapefile里总会带一个FID或者OBJECTID字段它是要素的唯一编号一般属于内部管理字段对下游系统来说往往是多余信息。所以工具里我默认把它过滤掉但也留了选项如果你确实需要主键勾一下就能带出来。另一个容易踩坑的是几何字段。一个shp文件里每个要素都对应着一条几何记录ArcGIS在属性表里显示的Shape字段是“几何对象”它不是普通的属性值。如果直接拿SearchCursor遍历Shape字段返回的是一个Geometry对象直接写进txt会报类型转换错误。我的做法是把几何字段单独拎出来做成一个开关——默认不导出如果需要导出坐标信息就让你选择是输出经纬度WKT格式适用于点/线/面都通用的文本表达还是只输出要素的面积和周长。字段类型的处理也很有讲究。数值型字段Short/Long/Float/Double导出后直接用原值没什么问题。字符串字段则需要注意内部的换行和制表符这些字符一旦混进txt就会破坏行与列的结构。日期字段Date在ArcGIS里存储的是datetime对象如果直接str()转换输出出来往往带微秒我在代码里统一格式化成“YYYY-MM-DD HH:MM:SS”这样对下游系统最友好。2.2 编码问题中文字段名和中文内容的双重考验做这个工具时我在编码上翻过车。场景是这样的一个全国路网shp字段名叫“道路名称”里面有“中山大道”“解放路”这种中文内容。我第一次用Python自带的open()函数直接写入文件当时电脑默认编码是GBK写出来的txt以GBK存储。发给用Linux服务器的开发同事后他那边程序用UTF-8读取满屏乱码。现在这个工具里编码参数我做了三个选项UTF-8带BOM、UTF-8无BOM、GBK。这里面为什么要把UTF-8再拆成带BOM和不带BOM两种因为Windows上的记事本打开UTF-8无BOM文件时默认会用GBK去解码导致开头出现“锘? ”这种乱码字符。如果你的txt要服务的是Windows环境的业务系统建议选UTF-8带BOM如果是Linux环境解析就选UTF-8无BOM因为BOM头会在文件开头增加三个字节有些解析器不认识会报错。还有一个涉及字段名的坑。shp文件的字段名在ArcGIS里显示中文但底层dbf的字段名机制决定了它实际上最多支持10个字符且一般存的是英文或拼音缩写。工具箱里如果要把字段名也作为表头输出到txt我建议直接用arcpy.ListFields()拿到每个字段的aliasName用别名做表头输出给非专业人员看时人家才看得懂。2.3 分隔符与数据引用规则一个容易被忽略的规范性细节TXT文件的说服力在很大程度上来自分隔符的选择。大家的直觉是用逗号也就是CSV格式。但真实业务数据里字符串字段中经常出现英文逗号比如“东湖高新开发区,一期”这种写法直接split(,)就会错位。我的工具里提供三个选项逗号、制表符Tab、管道符|。如果是我自己对接的系统我强烈建议用制表符。理由很简单业务数据中出现Tab的概率极低基本可以忽略。如果必须用逗号做CSV我就在代码里做一层引号包裹处理——凡是字符串字段首尾加上英文双引号内部出现的双引号替换成两个连续双引号CSV标准转义这样下游用Python的csv库或Excel打开都正确。这个细节虽然不起眼但真到了大数据量几十万条加上字段内容复杂的时候就是一个回不回工单的区别。除了分隔符行尾结束符也要留意。Windows环境推荐用\r\nLinux/Mac推荐只用\n。这并不只是规范问题之前有同事拿导出的txt去匹配Linux服务端的台账诡异的是最后一行总匹配不上找了一下午才发现是行尾符差异导致的。3. 实操过程与核心环节实现3.1 工具箱参数设计让用户只需三步完成转换编码上面的思路花的时间比较多但真正落实到工具层面还是要让使用简单。我最终在ArcGIS的自定义工具箱里挂了6个参数按顺序排列参数名类型方向说明输入要素图层Feature Layer输入可以是shp文件、要素类、图层输出TXT文件Text File输出填写导出路径导出字段列表MultiValue Field输入默认全选可手动勾选需要的字段分隔符选择String值列表输入Tab/逗号/管道符文本编码String值列表输入UTF-8带BOM/UTF-8无BOM/GBK是否输出几何信息Boolean输入勾选后额外输出WKT坐标列我当时考虑过增加一个“排序字段”参数后来放弃了因为SearchCursor的排序功能需要用到arcpy.SearchCursor的sql_clause在shapefile上支持有限sort字段必须存在于属性表且不支持NULL排序性价比不高真要排序可以让下游程序做没必要在导出环节增加复杂度。3.2 核心脚本实现与关键代码逐段解析下面是这个工具的核心Python代码我在ArcGIS Desktop 10.8的环境里实测通过ArcGIS Pro里除了参数获取方式稍有不同Pro用arcpy.mp代替部分界面函数但工具本身直接兼容基本可以直接复用。import arcpy import os import codecs def shp_to_txt(feature_layer, output_txt, fields_list, delimiter, encoding, output_geo): # 读取字段对象列表过滤掉不需要的内部字段 field_objects arcpy.ListFields(feature_layer) field_names [] field_aliases [] skip_names {FID, OBJECTID, Shape, Shape_Length, Shape_Area} for field in field_objects: if field.name in skip_names: continue if output_geo is False and field.type Geometry: continue field_names.append(field.name) field_aliases.append(field.aliasName if field.aliasName else field.name) # 根据用户选择的字段列表过滤 if fields_list and fields_list ! [#]: selected list(fields_list) field_names [fn for fn in field_names if fn in selected] field_aliases [fa for fa, fn in zip(field_aliases, field_names) if fn in selected] # 编码映射 enc_map { UTF-8带BOM: utf-8-sig, UTF-8无BOM: utf-8, GBK: gbk } enc enc_map.get(encoding, utf-8-sig) # 分隔符映射 sep_map { Tab: \t, 逗号: ,, 管道符: | } sep sep_map.get(delimiter, \t) with codecs.open(output_txt, w, enc) as f: # 写表头 header_cells [] for alias in field_aliases: if sep ,: header_cells.append(\ alias.replace(\, \\) \) else: header_cells.append(alias) f.write(sep.join(header_cells) \r\n) # 游标遍历要素 with arcpy.da.SearchCursor(feature_layer, field_names) as cursor: for row in cursor: cells [] for i, value in enumerate(row): # 日期格式统一 if isinstance(value, datetime.datetime): value value.strftime(%Y-%m-%d %H:%M:%S) # None值处理 if value is None: value text str(value) # 如果分隔符是逗号则特殊字符做引用包裹 if sep ,: if text.find(,) 0 or text.find(\) 0 or text.find(\n) 0 or text.find(\r) 0: text \ text.replace(\, \\) \ cells.append(text) f.write(sep.join(cells) \r\n)代码里三个细节值得单独说一下。第一使用codecs.open()而不是plain的open()。Python 2ArcGIS Desktop 10.x内置的Python环境是2.7对Unicode文件写入的管理比较脆弱codecs模块直接帮你处理编码转换写出来的文件不会出现“编码格式声明与实际不符”的问题。第二SearchCursor的上下文管理器with关键字一定要用。Cursor在ArcGIS里是占用系统资源的对象用完后必须显式释放否则在编辑会话过程中会出现“表被锁定”的诡异错误。with语句会在异常发生时也自动清理资源省心很多。第三None值的处理。属性表里未填写的字段在游标返回时是None如果你不做过滤直接str(None)输出文件里就会遍布“None”字符串下游解析时这玩意儿既不是NULL也不是空串非常尴尬。我的代码统一替换成空字符串干净得多。3.3 工具箱的安装、注册与调试过程记录脚本写完之后要把这个.py文件“挂”到ArcGIS的可执行环境中。最简单的做法是直接在ArcToolbox里右键→“添加工具箱”→“添加Python工具箱”文件后缀.pyt这个方式的好处是不需要额外注册扔在任意目录都能用缺点是参数面板的提示信息要写在.pyt的特殊函数里代码结构稍微绕一点。我的做法是传统的脚本工具方式更适合分享给别人在任意目录新建一个.tbx文件其实是XML格式但ArcGIS会识别。右键工具箱→添加→脚本把刚才的Python脚本路径指定上。在“参数”选项卡里逐个添加上表里那6个参数设置正确的类型和方向。在“源”选项卡里更新脚本文件路径。调试过程最花的不是代码逻辑而是参数传值。ArcGIS在把工具箱的字符串参数传给脚本时如果用户选择了图层实际传进来的是图层在内存中的引用路径比如C:\data\x.shp但如果用户没有选择图层而是直接拖了个shp文件进来传的值会带一个“Shapefile”前缀。我在脚本开头加了一行清理逻辑if isinstance(feature_layer, str) and feature_layer.startswith(Shapefile): feature_layer feature_layer.split(;)[0].split()[-1]这种老牌GIS软件的历史包袱新手往往想不到调试的时候容易在这块绕很久。4. 常见问题与排查技巧实录4.1 中文乱码、字段截断、OLE错误三个高频问题的逐一拆解这个工具箱从写出来到现在前后被同事、网友用过不少次——对后来我把它分享给了几个同行群。用得多了一些问题就浮现出来了我把最有代表性的三个问题列在这里并附上排查思路。问题一导出后的txt用记事本打开正常用Excel直接打开乱码。这大概率是编码选择了UTF-8无BOM而Excel在Windows中文环境下默认用ANSI也就是GBK解码。解决办法是改导出的编码参数为“UTF-8带BOM”或者不要在Excel里双击打开txt而是用“数据→自文本”导入并在导入向导里手动指定UTF-8编码。对大部分同事来说直接改参数更省事。问题二字段内容有换行符导出的txt行数比要素数多。这是字符串字段内部包含换行符导致的通常来自原始数据的复制粘贴。如果下游系统拿行数来校对数据就会出问题。我的处理是在代码里对字段值做一次replace(\r\n, \\n).replace(\r, \\n).replace(\n, \\n)把它变成可见的转义文本宁可让内容稍微难看一点也要保证行数对得上。问题三某些大字段比如描述类的文本导出不全中间被截断。遇到这种情况先确认是不是用了中间过渡比如经过Excel。我这个工具全程直接用ArcPy游标读dbf不会截断。如果还是遇到截断大概率是dbf本身字段长度限制shapefile属性表历史原因只支持255字符内的传统dbf格式但现在ArcGIS默认创建的已经支持更长文本。可以用arcpy.Describe()查看字段长度如果小于实际内容长度需要在ArcGIS里先对字段做转换。4.2 常见问题速查表现象可能原因解决方案txt打开乱码编码选择与下游解码方式不匹配根据下游系统选择UTF-8带BOM/GBK行数比要素多字段内容里有换行符开启代码里的换行转义逻辑字段内容包含分隔符导致列错位分隔符与数据冲突改用Tab分隔符或启用引号包裹导出中报错“The table was not found”输入的图层路径有特殊字符检查路径去掉中文命名或空格导出几十万条很慢游标逐条写入文件合并为批量写入或分块处理见4.3表头是英文不是中文字段别名未被启用确认aliasName存在且代码里用的是aliasNameShape字段报错无法写入几何对象不能直接str()关闭几何输出或用WKT函数转换4.3 大数据量导出优化的一点心得最后分享一个大数据量场景的优化点。当shp的记录超过50万条时逐行write的性能瓶颈会变得非常明显。我实测过20万条的属性表直接逐行写入需要大概两三分钟——不是不能忍但也不算快。后来我把写入逻辑改成了批量buffer_lines [] for i, row in enumerate(cursor): # 组装行... buffer_lines.append(line) if i % 10000 0: f.write(\r\n.join(buffer_lines) \r\n) buffer_lines [] if buffer_lines: f.write(\r\n.join(buffer_lines) \r\n)把每1万条攒成一个大字符串一次性写入磁盘IO次数大幅减少。我自己的测试数据从150秒降到了30秒上下。这个优化思路对一切“从数据库/文件系统读数据再写文本”的场景都通用如果你在Matlab、R、Pandas里处理类似导出需求也可以套用同样的“攒批写入”思路。坦白说这个工具箱并不复杂核心代码就100行左右。但我后来回看真正有价值的地方不在于代码本身而在于踩过的这些坑编码的坑、特殊字符的坑、字段类型转换的坑、大文件写入性能的坑。这些经验如果让使用者全部再踩一遍怕是半天就过去了。所以我把参数做成了可视化选项让非开发人员也能三分钟内完成一次shp到txt的转换这也是我写工具箱的初衷——把自己踩过的坑打包成别人的捷径。本文还有配套的精品资源点击获取