基于winnow 0.6.7,如何处理非行尾合法出现的EOF?
Winnow 0.6.7 处理EOF与末尾空行的最佳实践
核心问题分析
你遇到的无限循环问题,本质是eof解析器仅检查输入是否结束,不消耗任何内容。当把它放入repeat这类循环解析器时,每次匹配eof都不会推进输入位置,导致repeat反复尝试匹配,触发winnow的无限循环防护机制。
最佳实践方案
核心思路是拆分解析逻辑,避免将eof放入循环解析器中,同时覆盖所有边缘场景(带换行的行、无换行的末尾有效行、末尾空行):
1. 定义基础解析单元
首先实现行终止符、行内容的基础解析器:
use winnow::{ combinator::{alt, opt, terminated}, token::{newline, crlf}, Parser, }; // 定义行类型,根据你的需求调整 #[derive(Debug, PartialEq)] enum Line { Directive(String, u32, u32), Mode(u32, String), Empty, } // 匹配\n或\r\n行终止符 fn line_ending(input: &mut &str) -> winnow::PResult<()> { alt((crlf, newline)).parse_next(input).map(|_| ()) } // 解析非空行(根据你的指令格式实现) fn non_empty_line(input: &mut &str) -> winnow::PResult<Line> { alt(( // 解析.DIRECTIVE行,跳过末尾注释 (".DIRECTIVE ", winnow::ascii::dec_uint, " ", winnow::ascii::dec_uint, winnow::token::take_until("//")) .map(|(_, a, _, b, _)| Line::Directive(".DIRECTIVE".into(), a, b)), // 解析.MODE行 (".MODE ", winnow::ascii::dec_uint, " \"", winnow::token::take_until("\""), "\"") .map(|(_, num, _, content, _)| Line::Mode(num, content.into())), )).parse_next(input) } // 解析行内容:可以是非空行或空行(空字符串) fn line_content(input: &mut &str) -> winnow::PResult<Line> { alt(( non_empty_line, // 匹配空行(无任何内容) ().map(|_| Line::Empty), )).parse_next(input) }
2. 实现完整文件解析
将文件解析拆分为两部分:
- 批量解析所有带行终止符的行(包括空行)
- 单独处理末尾可能存在的无行终止符的行(包括有效行或空行)
- 最后用
eof确保输入完全消耗,避免残留内容
fn parse_file(input: &mut &str) -> winnow::PResult<Vec<Line>> { // 解析所有带行终止符的行:行内容 + 换行符 let mut lines = repeat(0.., terminated(line_content, line_ending)).parse_next(input)?; // 处理末尾无换行的行(可能是有效行或空行) if !input.is_empty() { lines.push(line_content.parse_next(input)?); } else { // 若输入已空,说明是末尾空行,需添加Empty行(不需要可省略此分支) lines.push(Line::Empty); } // 确保输入完全消耗,避免残留未解析内容 winnow::combinator::eof.parse_next(input)?; Ok(lines) }
3. 关键注意事项
- 绝对不要将
eof放入repeat、many0等循环解析器中,避免触发无限循环检查。 - 末尾空行的处理可按需调整:若不需要将纯EOF视为空行,直接去掉
else分支的lines.push(Line::Empty)即可。 - 行尾注释可通过
take_until("//")或skip_till规则跳过,无需额外复杂逻辑。
内容的提问来源于stack exchange,提问作者davidA
相关产品推荐
相关产品推荐

