简介Cassava 是一款轻巧但功能丰富的 CSV 编辑软件面向需要频繁创建、修改表格数据又不愿依赖大型办公套件的用户。它以文本编辑器式的操作编辑 CSV 表格并集成宏功能与简易电子表格能力适合日常数据处理、批量格式调整和轻量脚本自动化等场景。压缩包共 50 个文件约 1.78MB包含可执行主程序、25 个 HTML 帮助页面、17 个 CMS 宏脚本以及少量 CSV 样例、图片、说明文本和样式文件。帮助页面覆盖文件、编辑、视图、宏、选项等主题宏脚本涉及行列数调整、数据重排、数字转汉字、状态栏控制等示例便于边看文档边实践。目前已有 1444 人学习或下载。读者可获得可直接运行的软件、较完整的离线帮助文档和一批可参考的宏脚本既能快速上手 CSV 编辑也能借此了解 Cassava 的宏写法与选项配置减少自行摸索成本。1. 一份 200MB 的 CSV 双击就转圈Cassava 卡在哪个位置上游给了一份 180MB 的导出 CSV双击用表格软件打开转了两分钟弹窗告诉你超出行数上限换成通用文本编辑器能打开但一行几万字挤在一起改一个字段全靠肉眼数逗号改完还不敢保存怕把编码和换行一起冲掉。CSV 编辑这件事一直夹在“表格软件的智能推断”和“文本编辑器的无结构”之间——Cassava 填的就是这条缝一个面向 CSV/TSV 的结构化文本编辑器按单元格和行列操作不做类型推断不改你的前导零和长数字改完原样写回。它适合三类人跑完清洗脚本要做人工抽检的数据处理岗、需要手修一批导出文件或配置表的运维、以及被自动格式化坑过一次就不想再踩的人。2. 跑通第一份 CSV便携部署、打开路径与编码判定这一章先把环境按住。Cassava 是 Windows 上的轻量桌面工具主打便携和“打开即编辑”所以部署这件事本身没什么可讲的真正会卡住新手的是打开那一刻的三个选择分隔符是什么、编码是什么、首行算不算表头。这三个选错任意一个你看到的都不是你的数据。下面按部署、打开、编码三段走每段都给可执行的验证手段不靠猜。2.1 便携单文件的部署取舍这类工具我一般不放系统盘的程序目录。原因不玄学程序目录默认要管理员权限才能写而编辑器会在同目录或用户目录下留存最近打开、窗口布局一类的配置权限不足时表现为“设置改完重启就丢”你能排查半天。固定一个非系统路径用普通用户权限跑是最省事的做法。# 固定一个不带空格、不带中文的浅层目录避免后续路径处理出怪问题 $dst $env:USERPROFILE\Tools\Cassava New-Item -ItemType Directory -Force -Path $dst | Out-Null # 解压后先确认目录结构可执行文件与说明文件应该在同一层 Get-ChildItem $dst | Select-Object Name, Length, LastWriteTime这段的目的不是“装”是确认解压完整。便携包常见的问题是压缩包里套了一层同名目录你双击进去发现只有一个文件夹然后把快捷方式指到了错误层级。Get-ChildItem看一眼就知道可执行文件在不在最外层。LastWriteTime顺便能核对你拿到的是不是最新的那一份避免从旧压缩包里翻出来一个半年前的副本。还有一个现实问题单文件打包的桌面程序首次冷启动通常偏慢.NET 运行时初始化 单文件自解压第二次打开同一份文件会明显快很多。这不是卡死别在第一次启动的三秒里就把它关掉重装。如果遇到杀软拦截把整个Tools\Cassava目录加进白名单比每次点“允许”靠谱。2.2 打开第一份 CSV分隔符和引号规则打开方式有三条菜单里的打开、把文件直接拖到窗口上、系统里右键“打开方式”绑定。日常我用拖拽因为批量核对时一次拖三四个文件进来开成多标签更快。真正决定你看到什么的是打开对话框里的分隔符选项。逗号、制表符、分号、竖线是标配另外一般还能填自定义字符。选错的典型表现是整行挤在第一列或者每一列都被切成了半截。判断方法很简单看第一行的列数是否符合预期不符合就立刻换分隔符重开别在错的结构上开始改。在打开之前先造一份“探针文件”把分隔符规则里最容易翻车的四种情况一次性覆盖掉# gen_probe.py —— 造一份把 CSV 引号规则全踩一遍的探针文件 import csv rows [ [id, name, note, amount], [1, alpha, contains, comma, 12.50], # 字段内逗号 [2, beta, contains quote, 0], # 字段内双引号 [3, gamma, contains\nnewline, ], # 字段内换行 [001, delta, , 9999999999999999999], # 前导零 超长数字 ] with open(probe.csv, w, newline, encodingutf-8-sig) as f: csv.writer(f).writerows(rows)三个参数都不能省。newline是让 csv 模块自己管换行不加它Windows 上写出来的字段内换行会被转成\r\n\r\n文件直接坏掉。encodingutf-8-sig会写入 BOM正好用来验证工具对 BOM 的处理。第四行那两个值是故意的001用来验证前导零会不会被吃掉19 位数字用来验证长数字会不会被转成科学计数法——如果编辑器做了数值推断这一行就会变形而一个干净的 CSV 编辑器应该原样显示。打开probe.csv正常的渲染应该是四列第三行的高度明显比别的行高因为字段里有换行第四行第一列显示001而不是1。这三点都对说明分隔符和引号解析没问题可以往上放真实数据了。2.3 编码判定UTF-8、BOM 与 GBK 的分界乱码是这类工具最高频的求助点但九成情况不是工具的锅。老系统导出的 CSV 常是 GBK 编码且不带 BOM编辑器按 UTF-8 解码中文就变成一堆问号或方块。反过来带 BOM 的 UTF-8 被当成普通文本读第一列列名前面会多一个不可见字符你后面用脚本按列名取值就取不到——这个坑我在下游脚本里排查过整整一个下午。打开前先摸底别靠肉眼猜# sniff_encoding.py —— 只读前 256KB 做编码探测大文件也不会拖死 import chardet with open(legacy_export.csv, rb) as f: raw f.read(256 * 1024) result chardet.detect(raw) print(result) # 典型输出{encoding: GB2312, confidence: 0.99, language: Chinese}只读前 256KB 是刻意的全文件扫描在几百 MB 的文件上要几十秒而编码在整个文件里基本一致头部样本足够。confidence低于 0.7 时不要信这个结果直接去问上游“这份文件是什么编码导出的”比反复试错快。还有一点GB2312、GBK、GB18030是包含关系探测结果报 GB2312 时按 GB18030 打开覆盖面更广生僻字不会丢。在编辑器里确认编码后第一件事是另存一份 UTF-8而不是直接覆盖原文件继续改。原文件留着做回滚新文件作为工作副本。这个习惯能救你很多次——尤其是你不确定保存时会不会重新加上 BOM 的时候。2.4 保存时的编码继承很多编辑器保存时沿用“打开时的编码”也有默认写 UTF-8 的。两种行为都能用但你必须知道手上这个版本是哪一种。验证方法打开一份 GBK 文件改一个中文字段另存为新文件再用上面的探测脚本跑一遍。如果新文件变成了 UTF-8那下游所有按 GBK 读取的脚本都会挂。我的做法是固定一条规矩凡是要交给下游脚本的文件一律 UTF-8 带 BOM 或明确不带 BOM二选一并写进交接说明。带 BOM 的好处是 Excel 双击不乱码坏处是某些解析库会把 BOM 当成第一列列名的一部分不带 BOM 反过来。这个选择没有普适答案只有“你的下游能不能吃”。3. 编辑机制的三个关键点加载、选区与撤销栈搞清楚它是怎么把文件读进来的你才知道什么操作会慢、什么操作会丢数据。这一章拆三块大文件为什么能开、单元格级编辑怎么用、撤销和保存的边界在哪。3.1 为什么它敢直接开几百 MB表格软件打开 CSV 慢慢在它要做类型推断、建索引、算公式依赖还要为随机访问准备完整的内存结构。CSV 编辑器不需要这些它只要按行切分、按分隔符切列、只渲染可视区域内的那几十行。行是懒加载的滚动到哪渲染到哪所以打开 500MB 的文件首屏也是秒出。但这不代表没有上限。文件越大全量查找替换、全选复制、排序这类“必须扫全表”的操作就越慢内存也会跟着涨。自己压一遍比看别人说靠谱# gen_big.py —— 100 万行 8 列的压测文件用来摸自己机器的上限 import csv, random with open(big.csv, w, newline, encodingutf-8) as f: w csv.writer(f) w.writerow([fcol{i} for i in range(8)]) for i in range(1_000_000): w.writerow([ i, # 自增 id便于核对行数 fk{i % 997}, # 制造重复值测排序和查找 round(random.random(), 6), , # 空字段测空值渲染 x * 8, i * 2, ok, 2024-01-01, ])100 万行、8 列、字段都很短生成出来大概 4060MB。想要压到 500MB把行数乘十或者把x * 8改成x * 80。生成之后做三件事拖进编辑器看首屏耗时、滚到最底部看是否还能正常定位、用查找功能搜一个只有末尾才出现的值。第三步最能暴露问题——有些实现在大文件上的查找是伪流式的前面搜得快越往后越卡。内存怎么看打开任务管理器盯着这个进程的工作集从打开到滚到底部记录增长曲线。如果滚一遍内存翻了三倍还不回落说明缓存策略偏激进编辑大文件时要控制标签数量用完一个关一个。3.2 单元格编辑、矩形选区与多行粘贴它的编辑模型是“文本格子”不是“单元格对象”。这句话的含义是你在格子里输入的任何东西都按字符串存不会因为长得像数字就被转成数值也不会因为长得像日期就被重排。这是它相对表格软件最大的优势也是你必须自己保证数据规范的原因。常用操作我整理成一张表具体快捷键以你手上版本的菜单为准功能是通用的操作典型触发方式注意点进入单元格编辑双击或直接键入键入会覆盖原值双击是追加提交并移动Enter / Tab有的版本 Enter 下移、Tab 右移矩形扩选Shift 方向键跨列选择时不要跨过含换行的字段整列选中点列头整列替换前先备份无法局部撤销复制 / 粘贴CtrlC / CtrlV从 Excel 粘贴会带引号和格式撤销 / 重做CtrlZ / CtrlY撤销栈深度有限大批量操作前先另存表格里最需要盯的是最后两条。粘贴这一条从表格软件复制一片区域再粘进来剪贴板里是制表符分隔、带引号包裹的文本粘到逗号分隔的文件里会整片挤进一列。正确做法是粘之前先把目标文件的分隔符临时切成制表符粘完再切回来或者干脆用脚本转换别手动粘。撤销这一条更隐蔽。撤销栈一般只保留最近若干步而且保存动作通常会清空或截断撤销历史。也就是说你改了两百个格子、保存、发现改错了一列CtrlZ 已经回不去了。所以凡是批量改动先另存一个.bak副本再动手。3.3 脏标记、覆盖保存与外部并发修改标题栏上通常会有个星号或圆点表示“有未保存改动”这就是脏标记。看着不起眼但它是你判断“我到底改没改”的唯一依据。养成习惯关窗口前先看标记有标记就别直接点“不保存”。覆盖保存有一个必须知道的风险编辑期间如果另一个进程你的清洗脚本、同步盘、另一个编辑器也写了同一个文件你保存时会把对方的改动整个盖掉而且没有任何提示。这不是这个工具的缺陷是所有“打开—编辑—写回”模型的通病。常见做法是把工作流切成两段# 编辑前先冻结一份只读快照编辑期间脚本只写新文件不碰正在编辑的那份 cp data.csv data.csv.bak.$(date %Y%m%d%H%M) chmod 444 data.csv.bak.* # 可选防止手滑改快照 # 编辑完成后用校验脚本确认改动范围再决定是否替换原文件 python diff_check.py data.csv.bak.202401010930 data.csv先快照、再编辑、后校验三步的顺序不能变。快照放在同一个目录下文件名带时间戳出问题三秒就能回滚。chmod 444在 Windows 的 Git Bash 或 WSL 环境下有效纯 CMD 里跳过这步靠命名约定防止误改就够了。4. 查找替换与正则把清洗动作批量化手工改十个格子还行改两千个就得靠替换。这一章讲清楚字面量查找和正则查找的边界给几个能直接抄的清洗模式最后补一个替换后的校验动作——没有校验的批量替换等于闭着眼睛改生产数据。4.1 普通查找与正则的分界普通查找就是把输入框里的字符原样匹配点号就是点号星号就是星号。正则模式下这些字符变成元字符一个没转义的点号能匹配任意字符替换下去就是灾难。判断该用哪种很简单你要找的内容里含. * ? [ ] ( ) { } ^ $ | \中任意一个就要么转义、要么确认自己真的想用正则。比如找1.0这个版本号普通查找直接搜正则下得写成1\.0否则1x0、1 0都会被命中。另外两个开关也要留意大小写敏感、全词匹配。CSV 里的字段名大小写经常混用ID和id不是一回事全词匹配则能避免把status里的stat一起换掉。这两个开关的默认值各版本不统一替换前先拿一行数据试一遍。4.2 五个能直接抄的清洗正则下面这几条是我在数据核对里反复用的替换范围都建议先限定在选中列别一上来就全表替换场景查找正则替换为说明去字段首尾空白^\s|\s$空只对单元格内容生效不会动分隔符中文引号转半角[“”]转完要检查是否破坏了原有引号配对压缩连续空格{2,}单个空格别用\s{2,}会把换行也吃掉删除纯空行^\s*$空可能导致行尾残留换行删完看文件末尾日期补零^(\d{4})/(\d)/(\d)$\1-0\2-0\3只对“月日都是一位”的行生效混合格式会漏第一条要特别说明^和$在大多数编辑器里默认匹配的是“行首行尾”还是“整个文件首尾”行为不一样。如果替换完发现整行内容被删光了就是这个开关的问题切回多行模式重来。第三条的坑在\s包含换行用{2,}空格加量词更安全。第四条删空行之后一定要滚到文件末尾看一眼。有些实现删除整行时会留下一个孤立的换行符文件行数没变少但最后多出一个空行——下游脚本按行数做校验的直接报错。4.3 替换后必须跑一次全量校验批量替换最怕的不是替换错是错得看不出来。列数没变、行数没变但某个字段的值悄悄变了。所以每次替换完跑一遍指纹比对# diff_check.py —— 比对替换前后的行数、最大列数和抽样内容指纹 import csv, hashlib, sys def fingerprint(path): h hashlib.sha256() n_rows n_cols 0 with open(path, newline, encodingutf-8-sig) as f: reader csv.reader(f) for i, row in enumerate(reader): n_rows 1 n_cols max(n_cols, len(row)) if i and i % 1000 0: # 每 1000 行取一行进指纹 h.update(,.join(row).encode(utf-8)) return n_rows, n_cols, h.hexdigest()[:12] for p in sys.argv[1:]: rows, cols, fp fingerprint(p) print(f{p}\trows{rows}\tmax_cols{cols}\tfp{fp})用法是python diff_check.py before.csv after.csv对比三列输出。行数变了说明有整行被误删或误建最大列数变了说明分隔符被破坏只有指纹变了、前两项不变才是“预期内的字段级改动”这时候再抽几行肉眼确认。抽样指纹用i % 1000 0是刻意的全量哈希在千万行文件上太慢每千行取一行已经足够发现系统性改动而且结果可复现——同样的文件跑两次指纹一致你才能拿它当基线。encodingutf-8-sig保留着是为了让带 BOM 和不带 BOM 的文件都能正常读。5. 避坑与排查编码、分隔符、行尾的五类翻车下面五条都是我实际撞过的按“现象 → 原因 → 解决”写。你可以当成打开文件前的检查清单逐条对一遍能省掉大半返工。5.1 编码相关的两类乱码坑一打开全是问号或方块。现象中文列名变成????或者一串方块英文和数字正常。原因文件实际是 GBK/GB18030 编码编辑器按 UTF-8 解码。UTF-8 对无法识别的字节序列会替换成占位符所以规律是“ASCII 正常、非 ASCII 全废”。解决关掉文件重新打开时在编码下拉框里指定 GB18030确认显示正常后再另存为 UTF-8。别在原文件上直接改先另存一份工作副本。如果下拉框里没有你的编码先用前面那个探测脚本确认再去问上游要原始编码。坑二第一列列名取不到值。现象肉眼看着第一列是id脚本里row[id]报 KeyError或者打印出来前面带个看不见的方框。原因文件是 UTF-8 带 BOMBOM 这三个字节被当成了第一列列名的一部分。解决用十六进制看一眼文件头EF BB BF就是 BOM。处理后有两个方向编辑器里另存为“UTF-8 无 BOM”或者在读取端用encodingutf-8-sig让解析库自己吃掉 BOM。两个方向选一个统一执行别一半文件带一半不带那才是真正的麻烦。5.2 结构错位的两类问题坑三某一行的列数比别的行多整表错位。现象从某一行开始后面所有行的内容整体右移一列。原因那个字段里含有逗号但导出时没加引号包裹。CSV 规范里含分隔符的字段必须用双引号括起来很多手写导出或拼接字符串生成的“CSV”不遵守这条。解决先定位到出错行通常是第一个列数异常的行看原始文本里那个字段长什么样。修复有两种改上游导出逻辑用标准 CSV 库写或者在编辑器里手动给那个字段补上引号。前者治本后者救急——救急改完记得再跑一遍列数校验别改一处漏一处。坑四明明一行数据显示成两行。现象某条记录被拆成上下两行第二行只有一个孤零零的值。原因字段内含换行符且用的是裸\n而文件其他部分是\r\n。混合行尾在解析时会被当成两个记录边界。解决先统一行尾。用file命令或者十六进制查看器确认文件用的是 CRLF 还是 LF然后在编辑器里做一次全局的行尾规范化。规范之后再打开换行字段会被正确识别为“同一行内的高格子”。这一步做完还没好的就是引号缺失回到坑三的处理方式。5.3 保存后出现的隐形改动坑五脚本按行数校验失败了但肉眼看不出区别。现象替换或编辑保存后下游脚本报“行数不匹配”差 1。原因文件末尾的换行符被加上或去掉了。原文件最后一行没有换行结尾编辑器保存时补了一个或者反过来删空行时把最后的换行也删了。按行切分的脚本对这一点极其敏感。解决用wc -l或者读文件末尾几个字节确认。团队内定一条规矩写进交接说明——“文件以单个换行结尾”或“不以换行结尾”二选一并全员遵守。这条规矩看着小题大做但它能消掉一整类“昨天还好今天挂了”的悬案。另外提醒一句从表格软件复制数据粘贴进 CSV会把前导零、长数字、日期格式全部按表格软件的规则重写一遍。这一条没有技术解法只能靠流程约束——需要用表格软件看的看快照副本要改的就在 CSV 编辑器里改。6. 把它当人工复核台脚本清洗加手动抽检的固定链路脚本能处理 99% 的脏数据剩下 1% 永远需要人眼。我的固定做法是让脚本输出一份“复核清单”而不是让人在百万行里随机翻。清单里只放两类行结构异常的列数不对、关键字段为空和随机抽样的。异常行优先看抽样行用来验证“正常的数据是不是真的正常”。# make_review.py —— 从大文件里抽出异常行 200 行随机样本给人工复核 import csv, random with open(big.csv, newline, encodingutf-8) as f: rows list(csv.reader(f)) head, body rows[0], rows[1:] width len(head) # 结构异常列数不符或前三列有空值 bad [r for r in body if len(r) ! width or any(c.strip() for c in r[:3])] random.seed(42) # 固定种子复核结果可复现 sample random.sample(body, min(200, len(body))) with open(review.csv, w, newline, encodingutf-8-sig) as f: w csv.writer(f) w.writerow(head) w.writerows(bad[:300] sample) # 异常行封顶 300避免清单本身失控random.seed(42)别省。复核清单要能复现否则你改完一轮想对比“上次那批异常还在不在”结果抽样全变了。异常行封顶 300 条也是刻意的真出现几万条异常那说明脚本那一步就错了不该让人工去兜。清单生成后把它和原始文件一起拖进编辑器开成两个标签左右切换着看。改完一处立刻 CtrlS别攒一大批再存——撤销栈撑不住那么长的历史。全部改完后用第 4 章那个指纹脚本对比big.csv和review.csv的同名行确认只改了该改的地方再把复核结果合并回主流程。血泪经验是这条链路里最容易省掉的一步恰恰是“改完再跑一遍校验”。我吃过一次亏手工修了四十多行异常存盘、合并、上线第二天上游说有三个字段被整体右移了一列——原因是我在编辑器里补引号时多补了一个把后面的分隔符带进了字段里。四十行里错三行肉眼抽检根本发现不了。从那以后我每次走完人工复核都强制再跑一遍列数校验和指纹比对两个都过才允许合并。多花两分钟换掉一整晚的排查。希望帮到你。本文还有配套的精品资源点击获取