如何使用Rust nom库解析指定结构化文本得到Entry结构体集合
基于nom的正确解析实现
第一步:添加依赖
在Cargo.toml中添加nom库依赖:
[dependencies] nom = "7.1" # 可替换为最新稳定版本
第二步:完整实现代码
use nom::IResult; use nom::bytes::complete::{tag, take_until, is_not}; use nom::character::complete::line_ending; use nom::combinator::{opt, map, all_consuming}; use nom::sequence::{pair, terminated}; use nom::multi::many1; #[derive(Debug)] struct Entry { title: String, body: String, } /// 解析单个Entry fn parse_entry(i: &str) -> IResult<&str, Entry> { // 解析标题:取第一行,吃掉末尾换行 let parse_title = terminated(is_not("\r\n"), line_ending); // 解析正文:取到sep/之前的所有内容,吃掉sep/和后面可选的换行 let parse_body = terminated( take_until("sep/"), pair(tag("sep/"), opt(line_ending)) ); // 组合标题和正文解析,映射为Entry结构体 map( pair(parse_title, parse_body), |(title, body)| Entry { title: title.to_string(), // 修剪掉正文末尾多余的换行/空白 body: body.trim_end().to_string() } )(i) } /// 解析所有Entry组成的向量 fn parse_all_entries(i: &str) -> IResult<&str, Vec<Entry>> { // many1表示至少解析1个Entry,all_consuming要求所有输入都被解析完成 all_consuming(many1(parse_entry))(i) } fn main() { let data = r#"title1 title1 line1 title1 line2 sep/ title2 title2 line1 title2 line2 title2 line3 sep/ title3 title3 line1 sep/"#; match parse_all_entries(data) { Ok((_, entries)) => { println!("解析成功,共{}个条目:", entries.len()); for entry in entries { println!("---\n标题:{}\n正文:\n{}", entry.title, entry.body); } } Err(e) => println!("解析失败:{}", e) } }
修正说明
针对原有代码的问题做了如下调整:
- 修复
take_until的错误用法:take_until仅支持传入字符串/字符等匹配模式,不能直接传入alt组合的parser,改用is_not("\r\n")直接匹配换行前的所有内容实现标题提取 - 新增分隔符后换行处理:解析正文时额外匹配
sep/后的可选换行符,避免下一个条目解析出错 - 新增批量解析逻辑:用
many1组合子批量解析多个条目,all_consuming校验所有输入都被正常解析,无残留无效内容 - 移除不必要的regex相关依赖,仅用基础组合子即可满足解析需求
运行代码后会输出3个符合要求的Entry实例,完全匹配给定的解析规则。
内容的提问来源于stack exchange,提问作者Just a learner
相关产品推荐
相关产品推荐

