当值包含键和值之间的分隔符或对之间的分隔符时,如何从字符串中提取键和值?

Jam*_*mes 5 rust

如何有效地将字符串中的键值对提取到HashMapwhen

  • key后面总是跟:然后是值
  • value以 a 结尾,,后跟另一个key(有时是空格然后是key
  • value 可以包含 , :整个
  • value包括任何key
  • 的顺序 keys不固定
  • key名被称为

对于这些键值对

key1:value1, key2:this is, some value2, key3:anothe:r val,ue,
Run Code Online (Sandbox Code Playgroud)

它应该产生这个HashMap

"key1", "value1"
"key2", "this is, some value2"
"key3", "anothe:r val,ue"
Run Code Online (Sandbox Code Playgroud)

我已经尝试了以下代码,但仅将 a,作为分隔符是不好的,因为该值可以始终包含逗号。

"key1:value1, key2:this is, some value2, key3:anothe:r val,ue,"
    .split(",")
    .map(|kv| kv.splitn(2, ":").collect::<Vec<&str>>())
    .filter(|vec| vec.len() == 2)
    .map(|vec| (vec[0].trim().into(), vec[1].trim().into()))
    .collect()
Run Code Online (Sandbox Code Playgroud)

我的想法是提供一个键列表: ["key1", "key2", "key3"]用作分隔符

更新:

使用@Lucretiel 回答我想出了:

fn key_value<'a>(keys: &[&str], mut command: &'a str) -> HashMap<&'a str, &'a str> {
    let mut hashmap = HashMap::new();
    loop {
        if let Some(key) = key(&keys, &command) {
            command = &command[key.len() + 1..];

            let value = value(&keys, &command);
            let trim: &[_] = &[',', ' '];
            command = &command[value.len()..].trim_start_matches(trim);

            hashmap.insert(key, value);
        } else {
            break;
        }
    }
    hashmap
}

fn key<'a>(keys: &[&str], command: &'a str) -> Option<&'a str> {
    let regex = format!("^({}):", keys.join("|"));
    let regex = regex::Regex::new(&regex).expect("Invalid regex");
    match regex.shortest_match(&command) {
        Some(position) => Some(&command[..position - 1]),
        None => None,
    }
}

fn value<'a>(keys: &[&str], command: &'a str) -> &'a str {
    let regex = format!(r#",\s*({}):"#, keys.join("|"));
    let regex = regex::Regex::new(&regex).expect("Invalid regex");
    match regex.find(&command) {
        Some(position) => &command[..position.start()],
        None => command,
    }
}
Run Code Online (Sandbox Code Playgroud)

游乐场

Luc*_*iel 3

解决这个问题的实际代码并不简单,但它是可以做到的。有很多棘手的边缘情况,具体取决于您想要考虑的错误情况(例如,您是否要求已知键列表中的每个键都存在于要解析的输入字符串中?您是否允许重复的键?等等.)。基本算法如下所示:

  • 当键列表不为空时:
    • 找到启动字符串的键,匹配^{key}:. 这是当前的密钥。
      • 如果没有这个键,则出错;输入格式错误
    • 查找字符串中,\s*{key}:一个最早的键,匹配。这是下一个关键。
      • 如果没有更多的键,则字符串的其余部分就是该键的值
      • 否则,找到的两个键之间的所有内容都是当前值
    • 将(当前键,当前值)添加到哈希表中
    • 从密钥列表中删除当前密钥(假设您不接受重复的密钥)
    • 将(当前键,当前值)从输入字符串的前面切片
  • 一旦你没有键,返回哈希图

传统语法无法做到这一点;正如所呈现的那样,它非常含糊。但是,如果围绕扫描每个后续键来构造解析(假设键永远不会作为值中的子字符串出现),则可以成功解析此类输入。

所描述的算法以二次时间运行,但假设如果您创建一个复合正则表达式来同时搜索每个键,它应该可以简化为线性时间:

,\s*(key1|key2|key3|...):