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

Pri*_*six 9 string pattern-matching rust

我正在尝试得到这样的东西(不起作用):

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

我尝试了两个不同版本的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()
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
Run Code Online (Sandbox Code Playgroud)

两者似乎都是做一些非常常见的事情的可笑的漫长而复杂的方式,我错过了什么吗?

att*_*ona 6

您有一种用于选择问题的迷你语言:

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

如果您的要求到此为止,那么Regex基于解决方案的完美解决方案。

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

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

您的语言具有以下特征:

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

  • 最复杂的语句“goto {number}”由关键字( tag!) goto在( preceded!)数字( 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);
}
Run Code Online (Sandbox Code Playgroud)


use*_*064 5

你可以用str::split这个(游乐场

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
}
Run Code Online (Sandbox Code Playgroud)

将输出

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
}
Run Code Online (Sandbox Code Playgroud)


She*_*ter 5

您可以创建一个Regex捕获所有有趣组件的母版,然后构建Vec所有捕获的片段。这Vec然后可以匹配反对:

extern crate regex;

use regex::Regex;

fn main() {
    let input = "goto 4";
    let mut current_question_number = 0;

    // Create a regex that matches on the union of all commands
    // Each command and argument is captured
    // Using the "extended mode" flag to write a nicer Regex
    let input_re = Regex::new(
        r#"(?x)
        (next) |
        (prev) |
        (goto)\s+(\d+)
        "#
    ).unwrap();

    // Execute the Regex
    let captures = input_re.captures(input).map(|captures| {
        captures
            .iter() // All the captured groups
            .skip(1) // Skipping the complete match
            .flat_map(|c| c) // Ignoring all empty optional matches
            .map(|c| c.as_str()) // Grab the original strings
            .collect::<Vec<_>>() // Create a vector
    });

    // Match against the captured values as a slice
    match captures.as_ref().map(|c| c.as_slice()) {
        Some(["next"]) => current_question_number += 1,
        Some(["prev"]) => current_question_number -= 1,
        Some(["goto", x]) => {
            let x = x.parse().expect("can't parse number");
            current_question_number = x;
        }
        _ => panic!("Unknown Command: {}", input),
    }

    println!("Now at question {}", current_question_number);
}
Run Code Online (Sandbox Code Playgroud)