简介:CSV编辑软件-Cassava 是一款面向经常处理表格数据、又不想启动重型办公套件的用户推出的轻量级工具,兼顾纯文本编辑习惯与基础电子表格能力,可用于快速创建、修改和核对 CSV 文件,适合数据分析、配置维护、批量整理等场景。压缩包共 50 个文件,约 1.78MB,主体由 HTML 帮助文档、CMS 宏脚本、CSV 示例数据及可执行程序组成,另含少量图标、样式与说明文本,便于在本地直接运行并查阅功能说明。已有 1444 人学习下载。其价值在于把表格编辑、宏自动化与简易电子表格操作集中在一个小体积软件中,读者可通过内置说明了解菜单选项、备份策略、查找替换和宏引用等用法,并借助示例与脚本理解批量处理思路,降低手工编辑 CSV 时的重复劳动和出错概率。
1. 用 CSV 编辑软件 Cassava 处理导出文件:先别急着改内容
一份从业务系统导出的 CSV,用通用表格软件打开时前导零消失、长数字变成科学计数法,改完再导回去又担心格式被重写。这类场景里,CSV编辑软件 Cassava 的定位不是“另一个表格工具”,而是把 CSV 当文本编辑和表格视图两种视角来回切换的专用编辑器。它适合经常处理导入导出文件、需要对分隔符和编码保持敏感、又不想每次都用脚本重写一遍清洗逻辑的人。我的使用习惯是:先用它把文件读对,再用表格视图做批量编辑,最后把重复动作交给脚本。下面按“读对、改对、验对”的顺序拆开讲。
2. 把 CSV 读对:编码、分隔符与文本编辑视角的三个参数
2.1 为什么先锁读取参数,而不是先改内容
CSV 看起来像表格,底层却是纯文本。文本编辑视角下,真正决定“能不能读对”的不是列宽和颜色,而是编码、分隔符、引号规则和换行符。通用表格软件会自动推断类型,把00123变成123,把1E10当成科学计数法,甚至把以=开头的字段当公式解析。Cassava 这类 CSV 编辑器的价值在于:它更愿意把文件当文本处理,打开时让你明确选择分隔符和编码,保存时也不轻易重写字段类型。
我一般不会拿到文件就直接改。先复制一份样本,确认源文件编码和分隔符,再在 Cassava 里用固定参数打开。原因是 CSV 的“错列”往往不是内容错了,而是读取参数错了:分号被当成普通字符、引号里的逗号被当成分隔符、UTF-8 被按 GBK 解码,都会让整张表在视觉上错位。先锁参数,后面排序、筛选、正则替换才有意义。
提示:源文件编码不明时,不要反复用同一个编码打开再保存。每次错误保存都可能把乱码写死,先保留原始副本。
2.2 编码、分隔符、引号:打开前的参数表与探测脚本
不同构建的菜单命名有差异,认准三个参数即可:编码、分隔符、引号。编码优先试 UTF-8,乱码再试 GBK 或 GB18030;分隔符看文件头几行逗号、制表符、分号哪个更稳定;引号规则决定字段里的逗号会不会被切开。下面这张表是我打开陌生 CSV 前的检查顺序。
| 参数 | 常见取值 | 判断依据 | 设错后的现象 |
|---|---|---|---|
| 编码 | UTF-8、UTF-8 BOM、GBK、GB18030 | 中文是否乱码,首行是否有不可见 BOM | 中文变问号或方块,首列名带隐藏字符 |
| 分隔符 | 逗号、制表符、分号 | 每行字段数是否稳定 | 整行挤在一列,或列数忽多忽少 |
| 引号 | 双引号、无引号 | 字段内是否含分隔符或换行 | 带逗号的地址被切成两列 |
| 换行 | CRLF、LF | 文件来源和后续系统要求 | 末行合并,或导入方报格式错误 |
| 首行 | 有标题、无标题 | 第一行是字段名还是数据 | 排序把标题行卷进数据 |
拿不准编码和分隔符时,我会先用一段小脚本探测,不依赖第三方库,读前几 KB 就够。注意样本不要太短,太短时分隔符猜测容易不稳定。
# 探测 CSV 的编码与分隔符,只读样本,不改原文件 import csv import io from pathlib import Path SAMPLE_BYTES = 64 * 1024 # 只读前 64KB,避免大文件一次性载入 ENCODINGS = ["utf-8-sig", "utf-8", "gb18030", "gbk", "utf-16"] def sniff_csv(path: str) -> None: raw = Path(path).read_bytes()[:SAMPLE_BYTES] text = None used_encoding = None for enc in ENCODINGS: try: text = raw.decode(enc) used_encoding = enc break except UnicodeDecodeError: continue if text is None: print("未识别编码,建议用二进制方式检查或换样本") return # 去掉 BOM 影响后再猜分隔符 sample = text.lstrip("\ufeff") try: dialect = csv.Sniffer().sniff(sample, delimiters=",\t;|") delimiter = dialect.delimiter except csv.Error: delimiter = "," print(f"编码猜测: {used_encoding}") print(f"分隔符猜测: {repr(delimiter)}") print("前 3 行预览:") for i, line in enumerate(sample.splitlines()[:3], 1): print(i, repr(line[:160])) if __name__ == "__main__": sniff_csv("/data/input.csv")逻辑说明:脚本先按候选编码逐个尝试解码,成功即停,避免把 GBK 文件误判成 UTF-8;随后用csv.Sniffer在逗号、制表符、分号、竖线里猜分隔符,猜不出来就退回逗号并打印预览。参数说明:SAMPLE_BYTES控制读取量,大文件不必全读;ENCODINGS的顺序把utf-8-sig放前面,是为了识别带 BOM 的 UTF-8;delimiters里不要加太多候选,候选越多越容易误判。探测结果只作为打开 Cassava 时的参考,最终仍要人工看前几行列数是否稳定。
2.3 首轮保存与格式锁定:副本、换行和 BOM 的选择
读对之后,第一件事不是改数据,而是“另存副本并锁定格式”。我一般把原文件保留为只读,副本命名为带_clean后缀的工作文件,再在 Cassava 里确认保存参数。UTF-8 不带 BOM 适合大多数接口导入;如果下游系统对 BOM 敏感,就明确选不带 BOM;如果下游是较老的桌面导入工具,可能需要带 BOM 的 UTF-8。换行同理,CRLF 和 LF 不要凭感觉选,按下游要求来。
保存后做一次“回读验证”:关掉文件再重新打开,看列数、首行、末行和中文是否正常。这个动作花不了多少时间,但能挡住很多“改了半天,保存时编码又变了”的翻车。尤其要注意:有些编辑器会在保存时自动补 BOM 或改换行,如果 Cassava 的保存选项里有“保持原格式”一类设置,优先用它;没有就手动固定一次,后续都按这个格式走。
注意:字段以
=、+、-、@开头时,下游如果用通用表格软件打开,可能被当公式。清洗阶段可以加前缀单引号或空格,别等交付后才发现。
3. 表格视图下的 CSV 批量编辑:排序、筛选与正则替换的落地顺序
3.1 排序筛选前先做列结构快照
表格视图最容易让人放松警惕:看着像表格,就顺手排序、筛选、拖拽列。但 CSV 没有真正的“列类型”,排序结果是按文本还是按数值,取决于编辑器怎么解释。我的习惯是排序前先做一张列结构快照:记录总行数、总列数、每列前 5 个值、空值数量、疑似主键列。快照不用很正式,写在便签里也行,关键是排序后能对得上。
在 Cassava 里打开副本后,先看三处:第一行是不是标题、每行列数是否一致、末行有没有被换行符拆坏。列数不一致时不要急着排序,先回到第 2 章检查分隔符和引号。排序时优先选“文本排序”而不是自动数值排序,除非你确认该列全是数字且没有前导零。筛选也是同理:先筛选出空值行看一眼,再决定是删除还是补默认值,避免把“空字符串”和“缺失字段”混为一谈。
列结构快照可以记成下面这种检查表,后面第 6 章还会复用。
| 检查项 | 记录方式 | 异常处理 |
|---|---|---|
| 总行数 | 排序前后各记一次 | 行数变化说明有隐藏换行或末行问题 |
| 总列数 | 抽 10 行看字段数 | 不一致先查引号和分隔符 |
| 主键列 | 看是否唯一、是否为空 | 不唯一时不要用它做覆盖依据 |
| 空值列 | 统计空字符串数量 | 区分“空”与“未填写” |
| 前导零列 | 看是否被转成数字 | 需要文本编辑视角固定为字符串 |
3.2 正则替换的四个常用表达式与预览习惯
Cassava 的表格视图适合做正则替换,但正则替换最怕“全局一次点下去”。我一般先把替换范围限制在当前列或选中区域,再打开预览,确认命中行数合理才执行。下面四个表达式是我处理导出 CSV 时用得最多的,替换前都先备份副本。
| 目标 | 查找表达式 | 替换为 | 注意 |
|---|---|---|---|
| 去字段首尾空白 | `^\s+ | \s+$` | 空 |
| 删除纯空行 | ^\s*$ | 空 | 先确认空行不是有效分隔 |
| 合并多余空格 | [ \t]{2,} | 单个空格 | 不要误伤制表符分隔文件 |
| 给公式前缀加引号 | ^([=+\-@]) | '$1 | 只对文本字段列执行 |
操作顺序我固定为:选中目标列 → 打开替换 → 勾选正则 → 输入表达式 → 预览命中 → 执行 → 抽查前 20 行和后 20 行。预览命中数突然很大时先停,通常是表达式写宽了。比如^\s+|\s+$在包含换行的字段里可能匹配到不该动的位置,这时要先确认引号规则,再决定是否逐列处理。
3.3 多列填充和整列覆盖的边界
多列填充很爽,但边界要清楚:整列覆盖会抹掉原有差异,批量填充默认值会把“缺失”变成“已填写”。我一般只对三类列做整列操作:明确的状态列、可计算的派生列、需要统一前缀的编号列。主键列、金额列、日期列不做整列覆盖,改成“新增一列放清洗结果,再比对旧列”,确认无误后再替换。
在 Cassava 里做多列编辑时,先插入一个临时列,命名成_check_原列名,把清洗结果写进去。然后按主键排序,肉眼抽查 30 行,确认新旧列差异符合预期。确认后删除旧列、重命名新列,再保存副本。这个习惯看起来多一步,但比“改完发现整列错了再回滚”省时间。表格视图的优势是看得见,劣势是太容易顺手改,所以边界要提前定好。
4. 用宏和外部脚本接管重复劳动:Cassava 里做交互,脚本里做批量
4.1 宏的适用边界:录动作可以,复杂清洗别硬塞
Cassava 的宏入口在不同构建里命名有差异,但用途大体一致:把一串重复点击录成可重放步骤,比如固定分隔符打开、选中某列、执行一次替换、另存副本。宏适合“步骤稳定、字段位置稳定、人工确认环节少”的任务。复杂清洗不建议全塞进宏,因为宏对字段索引、弹窗焦点和当前选中区域很敏感,换一台机器或换一个文件就可能翻车。
我的分工是:Cassava 负责看、改、抽查;外部脚本负责可版本化的批量转换。宏只做“打开参数固定”和“最后保存副本”这类动作,真正的字段处理放到脚本里。这样即使宏失效,清洗逻辑还在,能复现。下面给一个不依赖 Cassava 宏 API 的 Python 脚本,用来做批量清洗和日志输出,Cassava 里再打开结果做人工复核。
4.2 一个可复用的 Python 清洗脚本:读入、字段处理、日志输出
脚本只做三件事:按固定编码和分隔符读入、对指定列做去空白和公式前缀处理、输出副本和日志。字段名用配置字典管理,避免把列索引写死。
# 批量清洗 CSV:固定编码/分隔符,按列名处理,输出副本和日志 import csv import re from pathlib import Path INPUT = Path("/data/input.csv") OUTPUT = Path("/data/output_clean.csv") LOG = Path("/data/clean.log") ENCODING = "utf-8-sig" # 按第 2 章探测结果修改 DELIMITER = "," # 制表符文件改成 "\t" QUOTECHAR = '"' # 需要去首尾空白的列名;按实际表头修改 TRIM_COLUMNS = {"name", "address", "remark"} # 需要防公式注入的列名 FORMULA_COLUMNS = {"name", "remark"} def clean_value(value: str, column: str) -> str: value = value.strip() if column in FORMULA_COLUMNS and re.match(r"^[=+\-@]", value): value = "'" + value return value def main() -> None: with INPUT.open("r", encoding=ENCODING, newline="") as fin, \ OUTPUT.open("w", encoding=ENCODING, newline="") as fout: reader = csv.DictReader(fin, delimiter=DELIMITER, quotechar=QUOTECHAR) writer = csv.DictWriter( fout, fieldnames=reader.fieldnames, delimiter=DELIMITER, quotechar=QUOTECHAR, quoting=csv.QUOTE_MINIMAL, lineterminator="\n", ) writer.writeheader() total = 0 changed = 0 for row in reader: total += 1 new_row = {} for col, val in row.items(): new_val = clean_value(val or "", col) if col in TRIM_COLUMNS else (val or "") new_row[col] = new_val if new_val != (val or ""): changed += 1 writer.writerow(new_row) LOG.write_text(f"total={total}, changed_cells={changed}\n", encoding="utf-8") print(f"完成:{total} 行,变更 {changed} 个单元格,输出 {OUTPUT}") if __name__ == "__main__": main()逻辑说明:用csv.DictReader按表头读入,列名不写死;clean_value先strip再去公式前缀,避免把普通负数误加引号;输出用QUOTE_MINIMAL,只有包含分隔符或引号时才加引号。参数说明:ENCODING和DELIMITER必须与第 2 章探测结果一致;TRIM_COLUMNS和FORMULA_COLUMNS按实际表头改;lineterminator="\n"是显式固定换行,下游要 CRLF 就改"\r\n"。日志只记总数和变更数,不记录字段内容,避免把敏感数据写进日志。
4.3 回到 Cassava 做人工复核:抽查列、抽样行、保存副本
脚本跑完不要直接交付。用 Cassava 打开输出文件,按固定编码和分隔符读入,先看三处:表头是否完整、列数是否与输入一致、中文是否正常。然后做抽样:前 20 行、后 20 行、随机中间 20 行,重点看被清洗的列有没有把有效内容误删。最后另存一份带日期后缀的副本,原输出保留不动。这样后面下游导入报错时,还能回到上一版对比。
复核时我一般会开两个窗口,一个放输入样本,一个放输出结果,按主键排序后逐段看差异。Cassava 的表格视图适合这种人工比对,但不适合直接改脚本处理过的批量字段。人工只处理“脚本规则覆盖不到”的例外行,处理完记录下来,下一轮再补进脚本。这样宏和脚本都不会变成黑匣子。
5. 避坑与排查:大文件、编码错列和覆盖保存的五条血泪记录
5.1 大文件与保存失败
现象一:打开几十万行 CSV 后滚动卡顿,排序像卡死。原因通常是编辑器试图一次性渲染全部行,或者文件里混入了大量空行和超长字段。解决方式是先用脚本按行切分样本,确认总行数和列数,再在 Cassava 里只打开需要处理的分片;排序前关闭预览类面板,按主键排序而不是全列排序。大文件不要反复全表正则替换,先筛出目标行再处理。
现象二:保存时提示写入失败,或者保存后文件大小变成 0。原因多半是原文件被另一个程序占用,或者保存路径没有写权限,也可能是磁盘空间不足。解决方式是先另存到新路径,确认能写再覆盖;覆盖前保留.bak副本。血泪经验是:不要在打开原文件的状态下直接“保存”,先“另存为”副本,确认内容完整后再替换。
现象三:排序后行号对不上,抽查时找不到原行。原因是排序改变了物理行序,而人工记录的是旧行号。解决方式是在排序前加一列_row_id,写入原始行号,排序后按_row_id回查。这个列在交付前删掉,不影响下游。
5.2 编码、列错位与公式注入
现象四:中文乱码,整列错位,越改越乱。原因是编码猜错后又在错误编码下保存,把乱码写死。解决方式是回到原始副本,用第 2 章的探测脚本确认编码,在 Cassava 里用正确编码重新打开,不要在上一次的错误结果上继续改。翻车最多的情况就是“看着能打开就以为对了”,结果保存一次坏一次。
现象五:下游打开后字段变成公式,或者前导零消失。原因是字段以=、+、-、@开头,或者数字列被下游当成数值。解决方式是在清洗阶段给公式前缀加单引号,给前导零列固定为文本格式;如果下游是通用表格软件,交付前先用文本编辑视角检查首字符。这个坑不一定会在本机复现,但到了下游就是黑匣子,提前加一层防护更稳。
注意:避坑章里的每条处理都建立在“有原始副本”的前提上。没有副本时,先复制文件再操作,别赌。
6. 验证与交付:抽样核对、差异比对和一条可复用的 CSV 清洗流水线
6.1 抽样核对表:列数、主键唯一性、空值和编码
交付前我不看“感觉对不对”,只看几个硬指标。下面这张表可以直接抄成检查清单,每次清洗完跑一遍。抽样不用全量,前 20 行、后 20 行、中间随机 20 行足够暴露大部分格式问题。
| 检查项 | 通过标准 | 不通过时先查 |
|---|---|---|
| 列数 | 每行字段数与表头一致 | 引号、分隔符、字段内换行 |
| 主键唯一性 | 目标列无重复、无空值 | 是否把空字符串当有效值 |
| 空值数量 | 与预期缺失量一致 | 是否误删空行或误填默认值 |
| 编码 | 中文正常,首行无隐藏字符 | BOM、UTF-8/GBK 混淆 |
| 公式前缀 | 危险首字符已处理 | =、+、-、@开头字段 |
| 末行 | 最后一行字段完整 | 文件末尾是否有孤立换行 |
6.2 差异比对与回滚:用行数、哈希和 diff 三件套
验证时我习惯先比行数和哈希,再做抽样 diff。行数变化要能解释:清洗规则如果会删空行,行数减少是正常的;如果规则只做替换,行数必须一致。哈希用来判断文件是否被意外重写,diff 用来看具体字段差异。下面这段命令不依赖复杂工具,在常见 shell 里就能跑。
# 行数、哈希、抽样 diff 三件套 wc -l /data/input.csv /data/output_clean.csv sha256sum /data/input.csv /data/output_clean.csv # 只比前 50 行,避免大文件刷屏 diff <(head -n 50 /data/input.csv) <(head -n 50 /data/output_clean.csv) || true逻辑说明:wc -l先看行数是否在预期范围内;sha256sum确认文件没有被意外覆盖;diff只比前 50 行,重点是看表头和前几行字段有没有错位。参数说明:路径按实际文件改;head -n 50的数字可以按样本量调整;|| true只是让 diff 有差异时命令不中断,实际使用时要看输出内容。差异比对完,把输出副本、日志和检查表放同一个交付目录,回滚时直接拿原始副本重跑脚本。
从那以后我每次改 CSV 都先复制副本、锁定编码和分隔符,再动手;清洗脚本和抽样表固定放在同一个目录,交付前强制走一遍行数、哈希和抽样 diff。工具不在多,能把“读对、改对、验对”串起来就够用。希望帮到你。
本文还有配套的精品资源,点击获取