将 Rust 中的正则表达式捕获转换为 HashMap?

6
我有一个未知名称和数量的已命名组的Regex。我想要将一个字符串与该正则表达式进行匹配,并获得一个HashMap<&str, &str>,其中组名为键,捕获的字符串为值。
我该如何做?我是否必须使用regex.captures(str).iter(),然后以某种方式映射、过滤和收集到一个映射中?还是有什么捷径可走?
2个回答

9

这很棘手,因为正则表达式可以有多个匹配项,并且每个捕获组在单个全局匹配中可能会被匹配多次。

也许可以像这样 (演示)

fn main() {
    let re = Regex::new(r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})").unwrap();
    let text = "2012-03-14";
    let caps = re.captures(text).unwrap();
    let dict: HashMap<&str, &str> = re
        .capture_names()
        .flatten()
        .filter_map(|n| Some((n, caps.name(n)?.as_str())))
        .collect();
    println!("{:#?}", dict);
}

那将输出:
{
    "y": "2012",
    "d": "14",
    "m": "03"
}

一旦你意识到捕获名称不是从Match本身获取,而是从父级Regex获取,代码就很简单。你需要执行以下操作:
  1. 调用capture_names(),它将返回一个Option<&str>的可迭代对象。
  2. flatten()这个可迭代对象,将删除None展开&str值。
  3. filter_map()这些捕获名称成为类型为(&str, &str)的元组列表(name, value)。需要使用filter来删除不存在的捕获(感谢@Anders)。
  4. collect()!这只是因为HashMap<K, V>实现了特性FromIterator<(K, V)>,所以一个(&str, &str)的迭代器可以收集成一个HasMap<&str, &str>

2
如果缺少命名组,这将导致恐慌。可以通过使用filter_map(|n| Some((n,captures.name(n)?.as_str())))而不是map来修复它。 - Anders
2
@Anders:哦,你说得对。我会按照你的建议进行修复。虽然更习惯用的解决方案是通过使用map(|n| (n, caps.name(n).map(|m| m.as_str())))来创建一个HasMap<&str, Option<&str>> - rodrigo
选项很好的建议。 - Anders

3
如果您有多个捕获结果,可以像这样将它们收集到一个列表中:
let all: Vec<HashMap<&str, &str>> = re
    .captures_iter("2012-01-12 , 2013-07-11 , 2014-09-14")
    .map(|caps| {
        re.capture_names()
            .map(|o| o.and_then(|n| Some((n, caps.name(n)?.as_str()))))
            .flatten()
            .collect()
    })
    .collect();

println!("{:#?}", all);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接