使用rust复刻linux经典命令:wc(文本统计工具)
文章目录
- 一、项目概述
- 项目简介
- 核心功能
- 技术栈
- 二、项目结构
- 三、各文件代码实现
- Cargo.toml(项目配置)
- src/counter.rs(统计器核心逻辑)
- src/file_handler.rs(文件读取与统计)
- src/main.rs(入口文件:参数解析与调度)
- 四、使用指南
- 基础命令(与原生 wc兼容)
- 输出示例
一、项目概述
项目简介
基于 Rust 实现 Linux 经典工具wc(word count)的核心功能,支持统计文件的字节数、字符数、单词数和行数。保持与原生 wc命令的兼容性,同时利用 Rust 的安全性和高效性,实现跨平台支持(Windows/macOS/Linux)。
核心功能
- 基础统计:行数(-l)、单词数(-w)、字节数(-c)、字符数(-m)
- 多文件支持:同时统计多个文件,输出单个文件统计结果与总和
- 默认模式:无参数时默认统计 行数 + 单词数 + 字节数(与原生wc一致)
- 跨平台兼容:适配不同系统的换行符(\n/\r\n)和编码(默认 UTF-8)
技术栈
- 开发语言:Rust(Edition 2021)
- 核心依赖:clap(命令行参数解析)
- 标准库模块:
std::fs(文件读取)、std::io(流处理)、std::path::Path(路径处理)
二、项目结构
rust_wc/
├── Cargo.toml # 项目配置与依赖
└── src/├── main.rs # 入口文件:参数解析、调度统计逻辑├── counter.rs # 核心逻辑:统计器实现(字节/字符/单词/行数)└── file_handler.rs # 文件处理:单文件读取与统计、多文件汇总

三、各文件代码实现
Cargo.toml(项目配置)
[package]name = "rust_wc"version = "0.1.0"edition = "2021"description = "Rust 实现的 wc 命令行工具"authors = ["Your Name"][dependencies]clap = { version = "4.4", features = ["derive"] } # 命令行参数解析
src/counter.rs(统计器核心逻辑)
/// 统计结果结构体(存储单个文件的统计数据)#[derive(Debug, Clone, Copy, Default)]pub struct CountResult {pub lines: u64, // 行数pub words: u64, // 单词数(以空白字符分隔)pub bytes: u64, // 字节数pub chars: u64, // 字符数(UTF-8 编码)}/// 统计器:处理字节流,累计统计结果pub struct WcCounter {result: CountResult,in_word: bool, // 标记是否处于单词中(用于单词数统计)}impl Default for WcCounter {fn default() -> Self {Self {result: CountResult::default(),in_word: false,}}}impl WcCounter {/// 创建新的统计器pub fn new() -> Self {Self::default()}/// 处理单个字节(更新字节数、行数、单词数)fn process_byte(&mut self, byte: u8) {// 字节数+1self.result.bytes += 1;// 行数统计:遇到 \n 则行数+1(兼容 Unix 换行符)if byte == b'\n' {self.result.lines += 1;}// 单词数统计:空白字符(空格/制表符/换行/回车)作为分隔符let is_whitespace = byte.is_ascii_whitespace();if !is_whitespace && !self.in_word {// 从非单词状态进入单词状态,单词数+1self.result.words += 1;self.in_word = true;} else if is_whitespace && self.in_word {// 从单词状态进入空白状态,标记退出单词self.in_word = false;}}/// 处理字符(更新字符数,仅在需要统计字符时调用)fn process_char(&mut self) {self.result.chars += 1;}/// 处理字节流(核心方法:逐字节处理,支持同时统计字节/行/单词;按需统计字符)pub fn process_stream<R: std::io::Read>(&mut self,mut reader: R,count_chars: bool, // 是否需要统计字符数) -> Result<(), Box<dyn std::error::Error>> {let mut buffer = [0u8; 1024]; // 1KB 缓冲区,提升读取效率while let Ok(n) = reader.read(&mut buffer) {if n == 0 {break; // 读取结束}// 处理缓冲区中的字节(统计字节/行/单词)for &byte in &buffer[..n] {self.process_byte(byte);}// 如需统计字符数:将缓冲区解析为 UTF-8 字符if count_chars {let s = std::str::from_utf8(&buffer[..n])?;for _ in s.chars() {self.process_char();}}}Ok(())}/// 获取最终统计结果pub fn get_result(&self) -> CountResult {self.result}}/// 汇总多个文件的统计结果pub fn sum_results(results: &[CountResult]) -> CountResult {results.iter().fold(CountResult::default(), |mut sum, res| {sum.lines += res.lines;sum.words += res.words;sum.bytes += res.bytes;sum.chars += res.chars;sum})}
src/file_handler.rs(文件读取与统计)
use std::fs::File;use std::path::Path;use crate::counter::{WcCounter, CountResult};/// 统计单个文件的核心逻辑/// - path: 文件路径/// - count_lines: 是否统计行数/// - count_words: 是否统计单词数/// - count_bytes: 是否统计字节数/// - count_chars: 是否统计字符数pub fn count_file(path: &Path,count_lines: bool,count_words: bool,count_bytes: bool,count_chars: bool,) -> Result<CountResult, Box<dyn std::error::Error>> {let file = File::open(path)?;let mut counter = WcCounter::new();// 仅在需要统计字符时传递 true(避免不必要的 UTF-8 解析开销)counter.process_stream(file, count_chars)?;Ok(counter.get_result())}/// 处理多个文件统计:输出单个文件结果 + 汇总结果pub fn count_multiple_files(paths: &[impl AsRef<Path>],count_lines: bool,count_words: bool,count_bytes: bool,count_chars: bool,) -> Result<(), Box<dyn std::error::Error>> {let mut all_results = Vec::with_capacity(paths.len());// 统计每个文件并输出结果for path in paths {let path = path.as_ref();let result = count_file(path, count_lines, count_words, count_bytes, count_chars)?;print_result(&result, path.file_name().unwrap().to_string_lossy(), count_lines, count_words, count_bytes, count_chars);all_results.push(result);}// 输出汇总结果(多文件时)if paths.len() > 1 {let total = crate::counter::sum_results(&all_results);print_result(&total, "total", count_lines, count_words, count_bytes, count_chars);}Ok(())}/// 格式化输出统计结果(对齐格式,与原生 wc 保持一致)fn print_result(result: &CountResult,filename: impl std::fmt::Display,count_lines: bool,count_words: bool,count_bytes: bool,count_chars: bool,) {let mut parts = Vec::new();// 按原生 wc 顺序:行数 → 单词数 → 字符数 → 字节数if count_lines {parts.push(format!("{:>8}", result.lines)); // 右对齐,占 8 字符}if count_words {parts.push(format!("{:>8}", result.words));}if count_chars {parts.push(format!("{:>8}", result.chars));}if count_bytes {parts.push(format!("{:>8}", result.bytes));}// 拼接结果 + 文件名println!("{} {}", parts.join(""), filename);}
src/main.rs(入口文件:参数解析与调度)
use clap::Parser;use std::path::Path;use crate::file_handler::{count_file, count_multiple_files};/// Rust 实现的 wc 命令(统计文件的行数、单词数、字节数、字符数)#[derive(Parser, Debug)]#[command(author, version, about, long_about = None)]struct Cli {/// 统计行数#[arg(short = 'l', long = "lines")]lines: bool,/// 统计单词数(以空白字符分隔)#[arg(short = 'w', long = "words")]words: bool,/// 统计字节数#[arg(short = 'c', long = "bytes")]bytes: bool,/// 统计字符数(UTF-8 编码)#[arg(short = 'm', long = "chars")]chars: bool,/// 要统计的文件路径(支持多个文件)#[arg(default_value = "-")] // 默认读取标准输入(与原生 wc 一致)files: Vec<String>,}fn main() -> Result<(), Box<dyn std::error::Error>> {let cli = Cli::parse();// 确定需要统计的维度:无参数时默认 行数+单词数+字节数let count_lines = cli.lines || !(cli.words || cli.bytes || cli.chars);let count_words = cli.words || !(cli.lines || cli.bytes || cli.chars);let count_bytes = cli.bytes || !(cli.lines || cli.words || cli.chars);let count_chars = cli.chars;// 处理标准输入(文件路径为 "-" 时)if cli.files == vec!["-".to_string()] {let mut counter = crate::counter::WcCounter::new();// 从标准输入读取流counter.process_stream(std::io::stdin(), count_chars)?;let result = counter.get_result();// 输出结果(无文件名)let mut parts = Vec::new();if count_lines { parts.push(format!("{:>8}", result.lines)); }if count_words { parts.push(format!("{:>8}", result.words)); }if count_chars { parts.push(format!("{:>8}", result.chars)); }if count_bytes { parts.push(format!("{:>8}", result.bytes)); }println!("{}", parts.join(""));return Ok(());}// 处理文件(转换为 Path 类型,过滤无效路径)let paths: Vec<_> = cli.files.iter().map(|s| Path::new(s)).collect();// 多文件统计(含单个文件)count_multiple_files(&paths, count_lines, count_words, count_bytes, count_chars)?;Ok(())}// 导入子模块mod counter;mod file_handler;
四、使用指南
基础命令(与原生 wc兼容)
# 编译项目cargo build --release# 进入编译产物目录(可选,方便直接执行)cd target/release# 1. 默认模式:统计行数+单词数+字节数(单个文件)
./rust_wc test.txt# 2. 统计行数(-l)
./rust_wc -l test.txt# 3. 统计单词数(-w)
./rust_wc -w test.txt# 4. 统计字节数(-c)
./rust_wc -c test.txt# 5. 统计字符数(-m,UTF-8 编码)
./rust_wc -m test.txt# 6. 组合统计(行数+字符数)
./rust_wc -l -m test.txt# 7. 多文件统计(自动输出总和)
./rust_wc test1.txt test2.txt# 8. 读取标准输入(管道使用)echo "Hello Rust" | ./rust_wc
cat test.txt | ./rust_wc -l
输出示例
# 单个文件默认模式
./rust_wc test.txt12 45 320 test.txt # 行数 单词数 字节数 文件名# 多文件统计(含总和)


这个项目保持与原生 wc 命令的参数格式和输出格式一致,降低使用成本,非常不错
想了解更多关于Rust语言的知识及应用,可前往华为开放原子旋武开源社区(https://xuanwu.openatom.cn/),了解更多资讯~
