在日常开发中,我们经常需要查看 CSV、TSV 等分隔符数据文件。直接 cat 出来的效果难以阅读,而打开 Excel 又太重。今天我们用 OpenCode 写一个命令行表格渲染工具——从标准输入读取分隔符数据,渲染成带边框的格式化表格。
最终效果如下:
$ echo "name,age,city\n张三,28,北京\n李四,32,上海" | ./table +------+-----+------+ | name | age | city | +------+-----+------+ | 张三 | 28 | 北京 | | 李四 | 32 | 上海 | +------+-----+------+
技术栈:Go 语言,零外部依赖,仅用标准库。代码约 100 行。
功能特性:
确保本地已安装:
> 如果没有 OpenCode,也可以用 VS Code + GitHub Copilot 替代,交互思路是一样的。
直接告诉 OpenCode 我们要什么,不需要手写一行代码:
> 我对 OpenCode 说:
> 帮我创建一个 Go 项目 table,功能是从标准输入读取分隔符数据,渲染成格式化表格输出到终端。包含 -d 参数指定分隔符类型,自动根据列宽对齐。
OpenCode 先执行 go mod init table 初始化项目,然后生成了第一版代码。它自己判断了关键逻辑——读取 stdin、按分隔符切分、计算列宽、画 ASCII 边框——然后直接写出了完整可用的程序。
关键点:描述需求时,说清楚"输入是什么、输出是什么、参数有什么",OpenCode 就能一次性生成接近可用的完整代码。
基础 ASCII 表格能跑了,但看起来比较朴素。我们让它支持几种不同的边框风格:
> 我对 OpenCode 说:
> 再加一个 -s 参数控制边框样式,支持 ascii、light 和 double 三种。light 用 Unicode 轻线框字符(┼├─│┤),double 用双线框(╬╠═║╣)。
OpenCode 理解了需求,用 map[string]struct{} 定义了三种边框样式配置,然后修改了画分隔线和画数据行的函数来使用对应字符。这次对话里 OpenCode 展现了一个很好的习惯——它先给出 diff 对比让我确认范围,再执行编辑。
关键点:当需求涉及"多个变体"时,用列表形式把每个变体说清楚,OpenCode 会准确映射。
> 我对 OpenCode 说:
> 加一个 -n 参数,当传入 -n 时认为输入没有表头行,所有行都当作数据行平等对待。
OpenCode 在渲染阶段根据 -n 标志决定:有表头时第一行用分隔线隔开,无表头时所有行统一处理。改动精准、没有破坏已有逻辑。
关键点:单次对话聚焦一个功能点。需求粒度越小,OpenCode 出错的概率越低。
功能完成后,加上健壮性:
> 我对 OpenCode 说:
> 处理三种边界情况:1)空行跳过;2)列数不一致的行自动补空;3)没有任何数据输入时输出错误提示。
OpenCode 在读取循环中加了 if line == "" { continue },在渲染时为缺失列做空字符串兜底,数据为空时输出错误并退出。
这一步体现了 AI 编程的优势:人工写代码时,边界情况往往被忽略;而 OpenCode 收到明确指令后,会一次性把所有边界处理到位。
> 我对 OpenCode 说:
> 帮我编译项目,然后用几条测试数据跑一下验证。
OpenCode 执行了 go build -o table 和几条测试命令,一切正常后给出了完整的使用示例。
从第一个需求到可运行工具,全程 4 次自然语言对话。没有手动写一行 flag.String 或 bufio.Scanner,也没有查一次 Go 文档。
package main
import (
"bufio"
"flag"
"fmt"
"os"
"strings"
)
func main() {
delimiter := flag.String("d", "comma", "delimiter: comma, tab, pipe, or semicolon")
noHeader := flag.Bool("n", false, "no header row")
style := flag.String("s", "ascii", "border style: ascii, light, or double")
flag.Parse()
delimiters := map[string]string{
"comma": ",",
"tab": "\t",
"pipe": "|",
"semicolon": ";",
}
sep, ok := delimiters[*delimiter]
if !ok {
fmt.Fprintf(os.Stderr, "unsupported delimiter: %s\n", *delimiter)
os.Exit(1)
}
borders := map[string]struct{ cross, left, mid, right, h, v string }{
"ascii": {"+", "+", "+", "+", "-", "|"},
"light": {"┼", "├", "┼", "┤", "─", "│"},
"double": {"╬", "╠", "╬", "╣", "═", "║"},
}
bd, ok := borders[*style]
if !ok {
fmt.Fprintf(os.Stderr, "unsupported style: %s\n", *style)
os.Exit(1)
}
scanner := bufio.NewScanner(os.Stdin)
var rows [][]string
maxCols := 0
for scanner.Scan() {
line := scanner.Text()
if line == "" {
continue
}
cols := strings.Split(line, sep)
for i := range cols {
cols[i] = strings.TrimSpace(cols[i])
}
rows = append(rows, cols)
if len(cols) > maxCols {
maxCols = len(cols)
}
}
if len(rows) == 0 {
fmt.Fprintln(os.Stderr, "no input data")
os.Exit(1)
}
colWidths := make([]int, maxCols)
for _, row := range rows {
for i, cell := range row {
runeLen := len([]rune(cell))
if runeLen > colWidths[i] {
colWidths[i] = runeLen
}
}
}
printSep := func(left, mid, right string) {
fmt.Print(left)
for i, w := range colWidths {
if i > 0 {
fmt.Print(mid)
}
fmt.Print(strings.Repeat(bd.h, w+2))
}
fmt.Println(right)
}
printRow := func(row []string) {
fmt.Print(bd.v)
for i := range colWidths {
cell := ""
if i < len(row) {
cell = row[i]
}
spaces := colWidths[i] - len([]rune(cell))
fmt.Printf(" %s%s %s", cell, strings.Repeat(" ", spaces), bd.v)
}
fmt.Println()
}
if !*noHeader {
printSep(bd.cross, bd.cross, bd.cross)
printRow(rows[0])
printSep(bd.left, bd.mid, bd.right)
rows = rows[1:]
} else {
printSep(bd.cross, bd.cross, bd.cross)
}
for _, row := range rows {
printRow(row)
}
printSep(bd.cross, bd.cross, bd.cross)
}
# 编译 go build -o table # 默认 CSV 输入 echo "name,age,city\n张三,28,北京\n李四,32,上海" | ./table # 管道分隔符 + Unicode 轻线框 echo "CPU|负载|温度\ni7|45%|72°C" | ./table -d pipe -s light # 无表头模式 echo "成功,200\n错误,500" | ./table -n
这个 100 行的表格渲染工具,从想法到可运行,全程只用了 4 次自然语言对话。OpenCode 的实际价值在于:
省去样板代码——项目初始化、flag 解析、错误处理这些机械工作,OpenCode 秒级完成。
即时的上下文理解——加边框样式时,OpenCode 自动关联到已有的 printSep 和 printRow 函数,无需手动指定位。
迭代友好——每次只提一个改动点,OpenCode 能精确编辑,不会破坏已写好的逻辑。
传统的"打开 IDE → 写代码 → 查文档 → 调试"循环,在这个项目里被压缩成了"说需求 → 看结果 → 说下一个需求"。对于这类百行级小工具,AI 编程的效率提升是数量级的。
如果你也在用 OpenCode,欢迎分享你的实践心得。