将字符串与模式匹配并提取值的好方法是什么?

时间:2018-05-24 09:02:17

标签: string rust pattern-matching

我正在尝试这样的事情(不起作用):

match input {
    "next" => current_question_number += 1,
    "prev" => current_question_number -= 1,
    "goto {x}" => current_question_number = x,
    // ...
    _ => status = "Unknown Command".to_owned()
}

我尝试了两个不同版本的Regex

go_match = regex::Regex::new(r"goto (\d+)?").unwrap();
// ...
match input {
    ...
    x if go_match.is_match(x) => current_question_number = go_match.captures(x).unwrap().get(1).unwrap().as_str().parse().unwrap(),
    _ => status = "Unknown Command".to_owned()
}

let cmd_match = regex::Regex::new(r"([a-zA-Z]+) (\d+)?").unwrap();
// ...
if let Some(captures) = cmd_match.captures(input.as_ref()) {
        let cmd = captures.get(1).unwrap().as_str().to_lowercase();
        if let Some(param) = captures.get(2) {
            let param = param.as_str().parse().unwrap();
            match cmd.as_ref() {
                "goto" => current_question_number = param,
            }
        } else {
            match cmd.as_ref() {
                "next" => current_question_number += 1,
                "prev" => current_question_number -= 1,
            }
        }
    } else {
        status = "Unknown Command".to_owned();
    }

两者看起来像是一种非常普遍的荒谬长而复杂的方式,我错过了什么?

2 个答案:

答案 0 :(得分:3)

您可以str::split使用此playground

fn run(input: &str) {
    let mut toks = input.split(' ').fuse();
    let first = toks.next();
    let second = toks.next();

    match first {
        Some("next") => println!("next found"),
        Some("prev") => println!("prev found"),
        Some("goto") => match second {
            Some(num) => println!("found goto with number {}", num),
            _ => println!("goto with no parameter"),
        },
        _ => println!("invalid input {:?}", input),
    }
}

fn main() {
    run("next");
    run("prev");
    run("goto 10");
    run("this is not valid");
    run("goto"); // also not valid but for a different reason
}

将输出

next found
prev found
found goto with number 10
invalid input "this is not valid"
goto with no parameter

答案 1 :(得分:2)

您可以使用迷你语言来提问:

  • 选择下一个问题
  • 选择上一页问题
  • 转到一个特定问题

如果您的要求在此处结束,则基于Regex的解决方案非常适合。

如果您的DSL可能会发展出基于解析器的解决方案,那么值得考虑。

解析器组合器nom是一个从基本元素开始构建语法的强大工具。

您的语言具有以下特征:

  • 它有三个替代语句(alt!): next prev goto \ d +

  • 最复杂的语句“goto {number}”由(tag!)a 前面的关键字(preceded! goto 组成号码digit!)。

  • 必须忽略任意数量的空格(ws!

这些要求在此实施中有所体现:

#[macro_use]
extern crate nom;

use nom::{IResult, digit};
use nom::types::CompleteStr;

// we have for now two types of outcome: absolute or relative cursor move
pub enum QMove {
    Abs(i32),
    Rel(i32)
}

pub fn question_picker(input: CompleteStr) -> IResult<CompleteStr, QMove> {
    ws!(input,
        alt!(
            map!(
                tag!("next"),
                |_| QMove::Rel(1)
            ) |
            map!(
                tag!("prev"),
                |_| QMove::Rel(-1)
            ) |
            preceded!(
                tag!("goto"),
                map!(
                    digit,
                    |s| QMove::Abs(std::str::FromStr::from_str(s.0).unwrap())
                )
            )
        )
    )
}

fn main() {
    let mut current_question_number = 60;
    let first_line = "goto 5";

    let outcome = question_picker(CompleteStr(first_line));

    match outcome {
        Ok((_, QMove::Abs(n))) => current_question_number = n,
        Ok((_, QMove::Rel(n))) => current_question_number += n,
        Err(err) => {panic!("error: {:?}", err)}
    }

    println!("Now at question {}", current_question_number);
}