我需要在每行解析一个文件
<string><space><int><space><float>
e.g。
abce 2 2.5
在C中我会这样做:
scanf("%s%d%f", &s, &i, &f);
如何在Rust中轻松和惯用地执行此操作?
答案 0 :(得分:17)
标准库不提供此功能。你可以用宏编写自己的。
macro_rules! scan {
( $string:expr, $sep:expr, $( $x:ty ),+ ) => {{
let mut iter = $string.split($sep);
($(iter.next().and_then(|word| word.parse::<$x>().ok()),)*)
}}
}
fn main() {
let output = scan!("2 false fox", char::is_whitespace, u8, bool, String);
println!("{:?}", output); // (Some(2), Some(false), Some("fox"))
}
宏的第二个输入参数可以是&amp; str,char或适当的闭包/函数。指定的类型必须实现FromStr特征。
请注意,我快速将它们放在一起,所以它没有经过彻底测试。
答案 1 :(得分:9)
你可以使用text_io
crate进行类似scanf的输入,模仿语法中的print!
宏
#[macro_use] extern crate text_io;
fn main() {
// note that the whitespace between the {} is relevant
// placing any characters there will ignore them but require
// the input to have them
let (s, i, j): (String, i32, f32);
scan!("{} {} {}\n", s, i, j);
}
您还可以将其分为3个命令:
#[macro_use] extern crate text_io;
fn main() {
let a: String = read!("{} ");
let b: i32 = read!("{} ");
let c: f32 = read!("{}\n");
}
答案 2 :(得分:2)
scan_fmt板条箱提供了另一种选择。它支持简单的模式,在选项中返回其输出,并且具有我认为比text_io更好的语法:
#[macro_use] extern crate scan_fmt;
fn main() {
let (s, i, j) = scan_fmt!("abce 2 2.5", "{} {d} {f}\n", String, i32, f32);
println!("{} {} {}", s.unwrap(), i.unwrap(), j.unwrap());
}
答案 3 :(得分:1)
除非出于某种原因需要复制scanf
解析事物的确切方法,否则我认为在大多数情况下(以及在大多数语言中),最佳答案是“仅使用regex
”。这是Rust示例:
use regex::Regex;
use std::io::prelude::*;
fn parse_line(s: &str) -> Option<(String, i32, f32)> {
let r = Regex::new(r"(\w+) (-?\d+) (-?[0-9]*.?[0-9]*)").unwrap();
let caps = r.captures(s)?;
let a = caps.get(1)?.as_str().to_string();
let b = caps.get(2)?.as_str().parse().ok()?;
let c = caps.get(3)?.as_str().parse().ok()?;
Some((a, b, c))
}
fn main() {
let stdin = std::io::stdin();
let stdin = stdin.lock();
for line in stdin.lines() {
println!("{:?}", parse_line(&line.unwrap()));
}
}
使用正则表达式确实引起了一些直接的问题,尤其是在浮点解析方面。您要支持负数吗?没有数字的小数点有效的浮点数吗?允许使用指数表示法吗?在一个肮脏的数据解析器中,您可能只支持数据在做什么。在实际的应用程序中,此解析器决定可能会成为您应用程序的重要API详细信息,因此一开始要保守一点。