在Rust中使用nom保留外部解析器的LocatedSpan
解决nom+nom_locate解析u{xxx}时保留完整LocatedSpan的问题
核心思路
要同时保留完整的u{xxx}范围和解析出Unicode字符,需要拆分两步处理:
- 用
recognize捕获整个u{xxx}的LocatedSpan - 在捕获到的span内部,单独解析十六进制数字并转换为Unicode字符
这样既不会丢失完整的位置信息,也能正常处理数字转换。
实现代码示例
use nom::{ bytes::complete::tag, character::complete::hex_digit1, combinator::{map, recognize, verify}, sequence::delimited, IResult, }; use nom_locate::LocatedSpan; type Span<'a> = LocatedSpan<&'a str>; /// 解析u{xxx}格式的Unicode转义,返回(完整span, 对应的char) fn parse_unicode_escape(input: Span) -> IResult<Span, (Span, char)> { // 第一步:用recognize捕获整个u{xxx}的完整span let recognize_escape = recognize( delimited( tag("u{"), // 验证十六进制数字长度在1-6之间 verify(hex_digit1, |s: &str| s.len() >= 1 && s.len() <= 6), tag("}"), ) ); // 第二步:在捕获的span内部解析数字并转成char map(recognize_escape, |full_span: Span| { // 提取中间的十六进制数字部分(跳过"u{"和"}") let digits = &full_span.fragment()[2..full_span.fragment().len()-1]; // 转换为u32码点 let code_point = u32::from_str_radix(digits, 16).unwrap(); // 转换为char(实际代码建议处理无效码点的错误) let ch = char::from_u32(code_point).unwrap(); (full_span, ch) })(input) } // 测试示例 fn main() { let input = Span::new("u{12a}"); let (remaining, (span, ch)) = parse_unicode_escape(input).unwrap(); println!("完整span范围:{}..{}", span.location_offset(), span.location_end_offset()); println!("解析出的字符:{}", ch); // 输出:ሪ }
关键细节说明
recognize的作用是将内部组合子匹配到的整个输入片段作为Span返回,而不是返回组合子的解析结果,这样就拿到了u{12a}的完整位置信息。- 在拿到完整span后,通过字符串切片提取中间的十六进制数字(跳过开头的
u{和结尾的}),再转换为Unicode字符。 - 实际项目中建议替换
unwrap()为错误处理逻辑,比如用nom的错误类型返回无效码点或格式错误。
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

