4个稳定版本
1.1.2 | 2022年5月5日 |
---|---|
1.1.1 | 2021年5月11日 |
1.1.0 | 2021年5月9日 |
1.0.0 | 2021年5月9日 |
在算法中排名793
每月下载量74
在 2 crates中使用
12KB
145 行
argmap
将命令行参数解析为哈希表和位置参数vec
此库不会填充自定义结构体,格式化帮助消息或转换类型。
您提供实现了ToString的项的迭代器,并将返回一个包含(args,argv)
的二元组
args
是一个包含位置参数的Vec<String>
argv
是一个映射到所有--key
值的HashMap<String,Vec<String>>
let (args,argv) = argmap::parse(std::env::args());
eprintln!["args={:?}", &args];
eprintln!["argv={:?}", &argv];
长(--file
)和短(-x
)选项,带或不带等号,成簇的短选项(例如:tar -xvf file.tgz
)以及非字母短选项(例如:tail -n1
)都受支持。您还可以有数字标志,但不能在短选项群中。
以下是一个可以向此解析器投掷的垃圾示例
$ cargo run -q --example parse -- -z 5 -y=6 -y8 --msg cool -7 --here=there \
-xvf file.tgz -qrs=1234 -n -555 one two three -abc+5 -c-6 -- four -z 0
args=["target/debug/examples/parse", "one", "two", "three", "four", "-z", "0"]
argv={"z": ["5"], "7": [], "y": ["6", "8"], "x": [], "v": [], "f": ["file.tgz"], "a": [], "b": [], "here": ["there"], "n": ["-555"], "qrs": ["1234"], "c": ["+5", "-6"], "msg": ["cool"]}
argv
哈希表的值是Vec<String>
而不是String
,因为您可能多次指定了相同的选项。如果您只想处理特定键的单个值,您可以在.and_then()
内部使用.first()
或.last()
。
let (args,argv) = argmap::parse(std::env::args());
let cool = argv.get("cool").and_then(|v| v.last());
布尔选项将被存储为空的vec![]
。您可以使用.contains_key()
来测试布尔标志是否存在
let (args,argv) = argmap::parse(std::env::args());
let show_help = argv.contains_key("h") || argv.contains_key("help");
HashMap
具有比任何参数解析器所能创建的更易用的字段访问方式,您可以使用您已经知道如何与之交互的知识,而不是学习特定的参数解析器API。
同样,命令行解析器通常具有的许多功能(例如别名和默认值)也可以从核心Option
类型的函数中获得,如.or_else()
、.and_then()
或.unwrap_or()
。
以下是一个更长的示例,因为如何将所有这些组合起来以有用的方式并不是显然的。这个示例是一个类似于wc
的词频统计程序,但为了简洁起见,它过于简化且不太准确。
use std::{io,fs::File};
type Error = Box<dyn std::error::Error+Send+Sync>;
type R = Box<dyn io::Read+Unpin>;
fn main() -> Result<(),Error> {
let (args,argv) = argmap::new()
.booleans(&[ "h", "help", "c", "bytes", "w", "words", "l", "lines" ])
.parse(std::env::args());
if argv.contains_key("h") || argv.contains_key("help") {
indoc::printdoc![r#"usage: {} {{OPTIONS}} [FILE]
Count the number of bytes, words, or lines in a file or stdin.
-i, --infile Count words from FILE or '-' for stdin (default).
-c, --bytes Show number of bytes.
-w, --words Show number of words.
-l, --lines Show number of lines.
-h, --help Show this message.
"#, args.get(0).unwrap_or(&"???".to_string())];
return Ok(());
}
let mut show_bytes = argv.contains_key("c") || argv.contains_key("bytes");
let mut show_words = argv.contains_key("w") || argv.contains_key("words");
let mut show_lines = argv.contains_key("l") || argv.contains_key("lines");
if !show_bytes && !show_words && !show_lines {
show_bytes = true;
show_words = true;
show_lines = true;
}
let stdin_file = "-".to_string();
let infile = argv.get("infile").and_then(|v| v.first()) // --infile=file
.or_else(|| argv.get("i").and_then(|v| v.first())) // -i file
.or_else(|| args.get(1)) // first positional arg after $0
.unwrap_or(&stdin_file) // default value: "-"
.as_str();
let mut stream: R = match infile {
"-" => Box::new(io::stdin()),
f => Box::new(File::open(f)?),
};
let mut buf = vec![0;4096];
let mut byte_count = 0;
let mut word_count = 0;
let mut line_count = 0;
loop {
let len = stream.read(&mut buf)?;
if len == 0 { break }
byte_count += len;
let s = std::str::from_utf8(&buf[0..len])?;
word_count += s.split_whitespace().count();
line_count += s.lines().count();
}
let mut outline = "".to_string();
if show_lines { outline += &format!["{:>4} ", line_count] }
if show_words { outline += &format!["{:>4} ", word_count] }
if show_bytes { outline += &format!["{:>4} ", byte_count] }
println!["{}", outline.trim_end()];
Ok(())
}
此示例还演示了如何告知解析器某些字段应解释为布尔值。目前,这是唯一可用的配置。
许多进行解析的库也提供帮助信息,但我更喜欢像上面的示例那样手动编写它们。这样,我可以更好地控制帮助信息的呈现和格式,使其尽可能有用。例如,某些标志可能仅在与其他标志的组合中使用才有意义,但这很难通过自动化工具提供的格式化选项来展示。而且如果帮助信息太长,您总是可以将其拆分到单独的文件中。