在日常开发中,经常需要快速了解一个文本文件的基本信息——比如 README 写了多少字、日志文件有多少行、某个模块的代码量等。今天我们用 Rust 写一个命令行文本统计分析工具,输入一个文件路径,输出字符数、词数、行数、句子数、段落数、字节数、预估阅读时间,以及高频词排名。
技术栈:Rust + 标准库(零外部依赖)
最终效果:
═══════════════════════════════ 📊 文本统计报告 ═══════════════════════════════ 文件: README.md ───────────────────────── 字符数: 3421 字节数: 3580 行数: 87 词数: 512 句子数: 45 段落数: 18 ───────────────────────── 预估阅读: 3 分钟 (250词/分钟) ═══════════════════════════════ 高频词 Top 10: 1. opencode 23 次 2. 项目 15 次 3. 使用 12 次 ... ═══════════════════════════════
下面我们来看看如何用 OpenCode 一步步完成这个工具,全程只写提示词,不手写一行代码。
我对 OpenCode 说:
> 帮我用 cargo 新建一个 Rust 项目,名字叫 textstat。然后实现一个命令行工具:接收一个文件路径作为参数,读取文件内容,统计总行数、字符数、词数、字节数并打印出来。只用标准库,不要加任何外部依赖。
OpenCode 的响应:
OpenCode 先后执行了 cargo new textstat 创建项目,然后直接生成了 src/main.rs。它用 std::fs::read_to_string 读取文件,用 .lines().count() 统计行数,.len() 统计字节数,.chars().count() 统计字符数,.split_whitespace().count() 统计词数,最后用 println! 格式化输出。整个过程只用了一条提示词,项目骨架就搭好了。
关键要点:OpenCode 理解"零外部依赖"这个约束,全程使用标准库,Cargo.toml 里 [dependencies] 保持为空。
我对 OpenCode 说:
> 增加词频统计功能。把每个词转小写,过滤掉长度小于 2 的纯标点或单字符,用 HashMap 计数,按频率降序排列,输出前 10 个高频词。
OpenCode 的响应:
OpenCode 在代码中引入 HashMap,对 split_whitespace() 得到的每个词做清洗——用 .chars().filter(|c| c.is_alphanumeric()) 过滤标点,转小写后只保留长度 ≥ 2 的词。然后将 HashMap 转为 Vec 并按值降序排序,取前 10 个输出。OpenCode 还主动处理了边界情况:如果词汇不满 10 个,就只输出实际数量。
我对 OpenCode 说:
> 再加入三个统计:1)段落数,按连续两个换行符(空行)分隔;2)句子数,按中英文句号、问号、感叹号分隔;3)预估阅读时间,按每分钟 250 词计算(向上取整)。
OpenCode 的响应:
OpenCode 准确地实现了段落统计(.split("\n\n").filter(|p| !p.trim().is_empty()))和句子统计(使用 .split() 的闭包参数,匹配 .、!、?、。、!、?)。阅读时间用 (word_count as f64 / 250.0).ceil() as u64 计算。三个新指标无缝集成到输出中,整个过程没有破坏已有的统计逻辑。
我对 OpenCode 说:
> 添加一个 --top N 命令行参数,让用户可以自定义显示多少个高频词,默认 10。另外用 ════ 和 ─── 分隔线美化输出格式,让报告看起来更专业。
OpenCode 的响应:
OpenCode 在 args 解析中加入了 --top 的处理逻辑,用迭代器的 position() 找到 --top 的位置,读取下一个参数作为 N 值。输出格式换成了带分隔线的布局,层次分明。整个修改只涉及参数解析和 print 语句,对核心逻辑零侵入。
我对 OpenCode 说:
> 帮我编译项目,然后用项目自身的 README.md(如果存在的话)做一次测试运行,看看输出效果。
OpenCode 的响应:
OpenCode 执行 cargo build --release 完成编译(约 3 秒),然后用 target/release/textstat README.md 运行测试,打印出完整的统计报告。所有指标计算正确,输出格式对齐美观。
最终 src/main.rs 共 68 行,零外部依赖:
use std::collections::HashMap;
use std::env;
use std::fs;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("用法: {} <文件路径> [--top N]", args[0]);
return;
}
let path = &args[1];
let top_n: usize = args.iter()
.position(|a| a == "--top")
.and_then(|i| args.get(i + 1))
.and_then(|n| n.parse().ok())
.unwrap_or(10);
let content = fs::read_to_string(path)
.expect("无法读取文件,请确认路径正确");
let lines = content.lines().count();
let chars = content.chars().count();
let bytes = content.len();
let paragraphs = content.split("\n\n")
.filter(|p| !p.trim().is_empty())
.count();
let sentences = content.split(|c: char| {
c == '.' || c == '!' || c == '?' || c == '。' || c == '!' || c == '?'
}).filter(|s| !s.trim().is_empty()).count();
let words: Vec<&str> = content.split_whitespace().collect();
let word_count = words.len();
let mut freq: HashMap<String, usize> = HashMap::new();
for w in &words {
let cleaned: String = w.chars()
.filter(|c| c.is_alphanumeric())
.collect::<String>()
.to_lowercase();
if cleaned.len() >= 2 {
*freq.entry(cleaned).or_insert(0) += 1;
}
}
let mut freq_vec: Vec<(String, usize)> = freq.into_iter().collect();
freq_vec.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0)));
let reading_minutes = (word_count as f64 / 250.0).ceil() as u64;
println!("═══════════════════════════════");
println!(" 📊 文本统计报告");
println!("═══════════════════════════════");
println!(" 文件: {}", path);
println!(" ─────────────────────────");
println!(" 字符数: {}", chars);
println!(" 字节数: {}", bytes);
println!(" 行数: {}", lines);
println!(" 词数: {}", word_count);
println!(" 句子数: {}", sentences);
println!(" 段落数: {}", paragraphs);
println!(" ─────────────────────────");
println!(" 预估阅读: {} 分钟 (250词/分钟)", reading_minutes);
println!("═══════════════════════════════");
println!(" 高频词 Top {}:", top_n.min(freq_vec.len()));
for (i, (word, count)) in freq_vec.iter().take(top_n).enumerate() {
println!(" {:2}. {:<18} {} 次", i + 1, word, count);
}
println!("═══════════════════════════════");
}
这个工具从零到可用,全程只用了 5 条提示词、不到 4 分钟。OpenCode 表现出了三个突出优势:
精准理解约束:说"零外部依赖",它就用标准库;说"默认 10 个",它就设 unwrap_or(10)
增量开发无缝:每次追加新功能,OpenCode 都做到对已有逻辑零破坏,修改精准到函数和变量级别
主动处理边界:词数不满 N 个、空段落、空句子等边界情况,OpenCode 都自动加了过滤条件
对比传统开发方式——查 API 文档、写代码、处理边界、调整格式——至少需要 15-20 分钟。AI 编程助手省去的不是"打字时间",而是"决策和查阅时间"。你只需要想清楚"要什么",剩下的交给 OpenCode。