WEBKT

Rust 命令行实战:打造 CSV 排序工具

109 0 0 0

今天,咱们来聊聊用 Rust 撸一个命令行工具,它可以读取 CSV 文件,然后按照你指定的某一列来排序,最后把排序后的结果给你吐出来。听起来是不是有点意思?这玩意儿在处理数据的时候,简直不要太方便!

需求分析

首先,咱得搞清楚要做什么。简单来说,就是这么几步:

  1. 读取 CSV 文件:得能把 CSV 文件里的数据读进来。
  2. 指定排序的列:用户得能指定按照哪一列来排序。
  3. 排序:按照指定的列,把数据排序好。
  4. 输出结果:把排序后的数据输出到屏幕或者文件。

命令行参数设计

命令行工具,参数是灵魂!咱得设计一套好用的参数,让用户用起来舒服。

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:是否倒序排序,默认是正序。

数据处理流程

参数搞定了,接下来就是数据处理的流程了:

  1. 解析命令行参数:用 Rust 的 clap 库来解析命令行参数,这玩意儿贼好用。
  2. 读取 CSV 文件:用 csv 库来读取 CSV 文件,方便快捷。
  3. 找到要排序的列:根据用户指定的列名,找到对应的列索引。
  4. 排序:把 CSV 数据读取到一个 Vec 里,然后用 sort_by 方法来排序。
  5. 输出结果:把排序后的数据输出到屏幕或者文件。

代码实现

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 命令行工具的开发。 以后有时间,可以考虑加上错误处理,性能优化,以及更多的功能。 比如,支持多列排序,支持不同的数据类型等等。

总而言之,学无止境,加油!

码农张三 Rust命令行工具CSV排序

评论点评