OpenCode 实践课:4 分钟用 Python 写出一个命令行 CSV 转 JSON 工具

项目介绍

数据分析工作中,CSV 和 JSON 是最常见的两种数据格式。CSV 适合表格查看,JSON 适合程序处理,两者互转是刚需。今天我们用 OpenCode 从头开发一个命令行 CSV→JSON 转换工具,支持自动类型推断表格预览,全程只用自然语言指挥 AI 写代码。

  • 技术栈:Python 3,纯标准库(csv / json / argparse)
  • 最终代码:约 90 行
  • 功能:读取 CSV → 自动识别数字/布尔值 → 输出 JSON → 支持表格预览

准备工作

  • Python 3.8+
  • 安装 OpenCode:npm install -g opencodeai(或参考 opencode.ai
  • 配置 API Key: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 分钟,零手写。