Rust 命令行实战:打造 CSV 排序工具
109
0
0
0
今天,咱们来聊聊用 Rust 撸一个命令行工具,它可以读取 CSV 文件,然后按照你指定的某一列来排序,最后把排序后的结果给你吐出来。听起来是不是有点意思?这玩意儿在处理数据的时候,简直不要太方便!
需求分析
首先,咱得搞清楚要做什么。简单来说,就是这么几步:
- 读取 CSV 文件:得能把 CSV 文件里的数据读进来。
- 指定排序的列:用户得能指定按照哪一列来排序。
- 排序:按照指定的列,把数据排序好。
- 输出结果:把排序后的数据输出到屏幕或者文件。
命令行参数设计
命令行工具,参数是灵魂!咱得设计一套好用的参数,让用户用起来舒服。
csvsort --input <input_file> --column <column_name> --output <output_file>
--input <input_file>:指定输入的 CSV 文件。--column <column_name>:指定按照哪一列排序。--output <output_file>:指定输出的文件,如果不指定,就输出到屏幕。
当然,还可以加一些其他的参数,比如:
--delimiter <delimiter>:指定 CSV 文件的分隔符,默认是,。--header:指定 CSV 文件是否有 header,默认有。--reverse:是否倒序排序,默认是正序。
数据处理流程
参数搞定了,接下来就是数据处理的流程了:
- 解析命令行参数:用 Rust 的
clap库来解析命令行参数,这玩意儿贼好用。 - 读取 CSV 文件:用
csv库来读取 CSV 文件,方便快捷。 - 找到要排序的列:根据用户指定的列名,找到对应的列索引。
- 排序:把 CSV 数据读取到一个
Vec里,然后用sort_by方法来排序。 - 输出结果:把排序后的数据输出到屏幕或者文件。
代码实现
Talk is cheap, show me the code! 下面是一个简单的实现,你可以参考一下:
use clap::Parser;
use csv::ReaderBuilder;
use std::error::Error;
use std::fs::File;
use std::process;
/// A simple CSV sorter
#[derive(Parser, Debug)]
#[clap(author = "Your Name", version, about = "Sorts CSV files by a specified column", long_about = None)]
struct Args {
/// Input CSV file
#[clap(short, long, value_parser)]
input: String,
/// Column to sort by
#[clap(short, long, value_parser)]
column: String,
/// Output file, if not specified, prints to stdout
#[clap(short, long, value_parser, default_value = "")]
output: String,
/// Delimiter character, default is ','
#[clap(long, value_parser, default_value = ",")]
delimiter: char,
/// Whether the CSV file has a header row
#[clap(long, action, default_value_t = true)]
header: bool,
/// Reverse the sorting order
#[clap(long, action)]
reverse: bool,
}
fn run() -> Result<(), Box<dyn Error>> {
let args = Args::parse();
// Open the input file
let file = File::open(args.input)?;
let mut rdr = ReaderBuilder::new()
.delimiter(args.delimiter as u8)
.has_headers(args.header)
.from_reader(file);
// Read the headers if they exist
let headers = rdr.headers()?.clone();
// Find the index of the column to sort by
let column_index = headers
.iter()
.position(|h| h == &args.column)
.ok_or("Column not found")?;
// Read all records into a vector
let mut records: Vec<csv::StringRecord> = rdr.records().collect::<Result<Vec<_>, _>>()?;
// Sort the records by the specified column
records.sort_by(|a, b| {
let val_a = a.get(column_index).unwrap_or("");
let val_b = b.get(column_index).unwrap_or("");
if args.reverse {
val_b.cmp(val_a)
} else {
val_a.cmp(val_b)
}
});
// Write the sorted records to the output
if args.output.is_empty() {
// Print to stdout
println!("{}", headers.join(&args.delimiter.to_string()));
for record in records {
println!("{}", record.join(&args.delimiter.to_string()));
}
} else {
// Write to file
let mut wtr = csv::WriterBuilder::new()
.delimiter(args.delimiter as u8)
.from_path(args.output)?;
wtr.write_record(&headers)?;
for record in records {
wtr.write_record(&record)?;
}
wtr.flush()?;
}
Ok(())
}
fn main() {
if let Err(err) = run() {
println!("error running example: {}", err);
process::exit(1);
}
}
代码解释
clap::Parser:这个宏可以自动帮你解析命令行参数,省时省力。csv::ReaderBuilder:用这个来读取 CSV 文件,可以指定分隔符和是否有 header。records.sort_by:这个方法可以按照指定的规则来排序Vec里的数据。
编译和运行
把代码保存为 csvsort.rs,然后在命令行里运行:
rustc csvsort.rs
./csvsort --input input.csv --column name --output output.csv
总结
用 Rust 写命令行工具,效率高,性能好,而且还很安全。希望这篇文章能帮你入门 Rust 命令行工具的开发。 以后有时间,可以考虑加上错误处理,性能优化,以及更多的功能。 比如,支持多列排序,支持不同的数据类型等等。
总而言之,学无止境,加油!