数据分析工作中,CSV 和 JSON 是最常见的两种数据格式。CSV 适合表格查看,JSON 适合程序处理,两者互转是刚需。今天我们用 OpenCode 从头开发一个命令行 CSV→JSON 转换工具,支持自动类型推断和表格预览,全程只用自然语言指挥 AI 写代码。
npm install -g opencodeai(或参考 opencode.ai)export OPENAI_API_KEY=sk-xxx我对 OpenCode 说的第一句话:
> 用 Python 写一个命令行工具,读取 CSV 文件并输出 JSON,支持 -o 指定输出文件、-d 指定分隔符、--indent 控制缩进。用 argparse,只使用标准库。
OpenCode 立刻生成了一个完整的 csv2json.py,包含 argparse 参数解析、csv.DictReader 读取、json.dumps 输出。核心逻辑不到 30 行,直接能跑:
echo "name,age,city
张三,28,深圳
李四,32,北京" > test.csv
python csv2json.py test.csv
# [{"name": "张三", "age": "28", "city": "深圳"}, ...]
要点:描述需求时明确指定技术选型(argparse + 标准库),OpenCode 会严格遵循,不会引入多余依赖。
CSV 默认把所有值当字符串,但实际数据中 age 应该是数字。我对 OpenCode 说:
> 给这个工具加个 --type-infer 参数,开启后自动将整数字符串转为 int,浮点数转为 float,"true"/"false" 转为 bool,"null"/"" 转为 None。
OpenCode 在文件中插入了一个 infer_type() 函数,并修改了 csv_to_json() 的转换逻辑:
def infer_type(value):
if value == '':
return None
if value.lower() == 'true':
return True
if value.lower() == 'false':
return False
if value.lower() in ('null', 'none'):
return None
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
return value
现在加上 -t 参数,age 字段就变成了数字类型:
python csv2json.py test.csv -t
# [{"name": "张三", "age": 28, "city": "深圳"}, ...]
要点:修改已有代码时,直接说"给 xx 加一个 xx 功能",OpenCode 能精准定位并局部修改,不会破坏已有逻辑。
有时候不想看 JSON,只想快速浏览数据长什么样。我对 OpenCode 说:
> 加一个 -p / --preview 参数,用 ASCII 表格打印 CSV 内容,自动计算列宽对齐,超过 10 行截断并提示总行数。
OpenCode 写了一个 preview_table() 函数,效果如下:
python csv2json.py test.csv -p +------+-----+------+ | name | age | city | +------+-----+------+ | 张三 | 28 | 深圳 | | 李四 | 32 | 北京 | +------+-----+------+
要点:描述 UI 效果时越具体越好——"自动计算列宽对齐"、"超过 10 行截断"——AI 按描述实现的准确率很高。
补充参数说明、文件不存在的错误提示、--headers 自定义表头等功能。我对 OpenCode 说:
> 加上 -H 自定义表头、-n 限制输出行数,输入文件不存在时打印错误并退出码为 1。
至此,这个 CSV 转 JSON 工具已经实用级别了。
#!/usr/bin/env python3
"""CSV to JSON converter with type inference and table preview."""
import argparse
import csv
import json
import sys
from pathlib import Path
def infer_type(value):
if value == '':
return None
if value.lower() == 'true':
return True
if value.lower() == 'false':
return False
if value.lower() in ('null', 'none'):
return None
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
return value
def csv_to_json(csv_path, delimiter=',', encoding='utf-8', type_infer=False,
headers=None, limit=None):
with open(csv_path, 'r', encoding=encoding, newline='') as f:
if headers:
reader = csv.DictReader(f, fieldnames=headers, delimiter=delimiter)
else:
reader = csv.DictReader(f, delimiter=delimiter)
rows = []
for i, row in enumerate(reader):
if limit is not None and i >= limit:
break
if type_infer:
row = {k: infer_type(v) for k, v in row.items()}
rows.append(row)
return rows
def preview_table(data, max_rows=10):
if not data:
print("(empty)")
return
headers = list(data[0].keys())
col_widths = {h: len(h) for h in headers}
for row in data[:max_rows]:
for h in headers:
col_widths[h] = max(col_widths[h], len(str(row.get(h, ''))))
sep = '+' + '+'.join('-' * (col_widths[h] + 2) for h in headers) + '+'
header_line = '|' + '|'.join(
f' {h:<{col_widths[h]}} ' for h in headers) + '|'
print(sep)
print(header_line)
print(sep)
for row in data[:max_rows]:
line = '|' + '|'.join(
f' {str(row.get(h, "")):<{col_widths[h]}} ' for h in headers) + '|'
print(line)
print(sep)
if len(data) > max_rows:
print(f"... and {len(data) - max_rows} more rows")
def main():
parser = argparse.ArgumentParser(
description='Convert CSV files to JSON with optional type inference.'
)
parser.add_argument('input', help='Input CSV file path')
parser.add_argument('-o', '--output', help='Output JSON file')
parser.add_argument('-d', '--delimiter', default=',', help='CSV delimiter')
parser.add_argument('-i', '--indent', type=int, default=2,
help='JSON indent (0 for compact)')
parser.add_argument('-e', '--encoding', default='utf-8', help='Encoding')
parser.add_argument('-t', '--type-infer', action='store_true',
help='Auto-detect numbers and booleans')
parser.add_argument('-H', '--headers', nargs='*', help='Custom headers')
parser.add_argument('-n', '--limit', type=int, help='Limit to N rows')
parser.add_argument('-p', '--preview', action='store_true',
help='Preview as ASCII table')
args = parser.parse_args()
input_path = Path(args.input)
if not input_path.exists():
print(f"Error: '{args.input}' not found", file=sys.stderr)
sys.exit(1)
data = csv_to_json(
args.input,
delimiter=args.delimiter,
encoding=args.encoding,
type_infer=args.type_infer,
headers=args.headers,
limit=args.limit,
)
if args.preview:
preview_table(data)
return
json_str = json.dumps(data, ensure_ascii=False,
indent=args.indent or None)
if args.output:
Path(args.output).write_text(json_str, encoding='utf-8')
print(f"Converted {len(data)} rows → {args.output}")
else:
print(json_str)
if __name__ == '__main__':
main()
整个开发过程,我全程没有手写一行代码,所有逻辑通过自然语言描述让 OpenCode 完成。从初始骨架到类型推断、表格预览、错误处理,每一步都是"说需求 → 看结果 → 说下一个需求"的循环。
对比传统开发方式:
| 环节 | 手写 | 用 OpenCode |
|------|------|-------------|
| 项目搭建 | 5-10 分钟 | 30 秒 |
| 核心逻辑 | 15-20 分钟 | 1 分钟 |
| 功能迭代 | 每次 5-10 分钟 | 每次 30 秒 |
| 调试修复 | 不确定 | 精准描述即可 |
最大的感受是:AI 不是替代你思考,而是消除编码摩擦力。你知道要什么、知道怎么做,但不用亲自敲键盘,把精力集中在需求和设计上。对于脚本工具、原型验证这类场景,效率提升非常明显。
如果你还没试过 OpenCode,不妨从一个小工具开始——就像今天这个 CSV 转 JSON 一样,4 分钟,零手写。