使用解析器组合器解析带有转义字符的字符串?
Using parser-combinators to parse string with escaped characters?
我正在尝试使用 Rust 中的 combine 库来解析字符串。我试图解析的真实数据看起来像这样:
A79,216,0,4,2,2,N,"US\"PS"
所以在该数据的末尾是一个用引号引起来的字符串,但该字符串也将包含转义字符。我不知道如何解析其他引号之间的那些转义字符。
extern crate parser_combinators;
use self::parser_combinators::*;
fn main() {
let s = r#""HE\"LLO""#;
let data = many(satisfy(|c| c != '"')); // Fails on escaped " obviously
let mut str_parser = between(satisfy(|c| c == '"'), satisfy(|c| c == '"'), data);
let result : Result<(String, &str), ParseError> = str_parser.parse(s);
match result {
Ok((value, _)) => println!("{:?}", value),
Err(err) => println!("{}", err),
}
}
//=> "HE\"
上面的代码将成功解析该字符串,但显然会在中间的转义字符上失败,最后打印出 "HE\"
。
我想更改上面的代码,以便它打印 "HE\\"LLO"
.
我该怎么做?
我有一个主要功能性的 JSON 解析器作为解析器组合器的基准,它解析这种转义字符。我在其中包含了一个 link 和下面的稍微简化的版本。
fn json_char(input: State<&str>) -> ParseResult<char, &str> {
let (c, input) = try!(satisfy(|c| c != '"').parse_state(input));
let mut back_slash_char = satisfy(|c| "\"\nrt".chars().find(|x| *x == c).is_some()).map(|c| {
match c {
'"' => '"',
'\' => '\',
'n' => '\n',
'r' => '\r',
't' => '\t',
c => c//Should never happen
}
});
match c {
'\' => input.combine(|input| back_slash_char.parse_state(input)),
_ => Ok((c, input))
}
}
由于此解析器可能会消耗 1 或 2 个字符,因此使用原始组合器是不够的,因此我们需要引入一个函数,该函数可以在被解析的字符上分支。
我 运行 遇到了同样的问题,最终得到以下解决方案:
(
char('"'),
many1::<Vec<char>, _>(choice((
escaped_character(),
satisfy(|c| c != '"'),
))),
char('"')
)
或者换句话说,字符串由 "
后跟 many
escaped_characters
或任何不是结束符 "
的内容分隔,并由结束语 "
.
这是我如何使用它的完整示例:
pub enum Operand {
String { value: String },
}
fn escaped_character<I>() -> impl Parser<Input = I, Output = char>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
char('\'),
any(),
).and_then(|(_, x)| match x {
'0' => Ok('[=11=]'),
'n' => Ok('\n'),
'\' => Ok('\'),
'"' => Ok('"'),
_ => Err(StreamErrorFor::<I>::unexpected_message(format!("Invalid escape sequence \{}", x)))
})
}
#[test]
fn parse_escaped_character() {
let expected = Ok(('\n', " foo"));
assert_eq!(expected, escaped_character().easy_parse("\n foo"))
}
fn string_operand<I>() -> impl Parser<Input = I, Output = Operand>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
char('"'),
many1::<Vec<char>, _>(choice((
escaped_character(),
satisfy(|c| c != '"'),
))),
char('"')
)
.map(|(_,value,_)| Operand::String { value: value.into_iter().collect() })
}
#[test]
fn parse_string_operand() {
let expected = Ok((Operand::String { value: "foo \" bar \n baz [=11=]".into() }, ""));
assert_eq!(expected, string_operand().easy_parse(r#""foo \" bar \n baz [=11=]""#))
}
我正在尝试使用 Rust 中的 combine 库来解析字符串。我试图解析的真实数据看起来像这样:
A79,216,0,4,2,2,N,"US\"PS"
所以在该数据的末尾是一个用引号引起来的字符串,但该字符串也将包含转义字符。我不知道如何解析其他引号之间的那些转义字符。
extern crate parser_combinators;
use self::parser_combinators::*;
fn main() {
let s = r#""HE\"LLO""#;
let data = many(satisfy(|c| c != '"')); // Fails on escaped " obviously
let mut str_parser = between(satisfy(|c| c == '"'), satisfy(|c| c == '"'), data);
let result : Result<(String, &str), ParseError> = str_parser.parse(s);
match result {
Ok((value, _)) => println!("{:?}", value),
Err(err) => println!("{}", err),
}
}
//=> "HE\"
上面的代码将成功解析该字符串,但显然会在中间的转义字符上失败,最后打印出 "HE\"
。
我想更改上面的代码,以便它打印 "HE\\"LLO"
.
我该怎么做?
我有一个主要功能性的 JSON 解析器作为解析器组合器的基准,它解析这种转义字符。我在其中包含了一个 link 和下面的稍微简化的版本。
fn json_char(input: State<&str>) -> ParseResult<char, &str> {
let (c, input) = try!(satisfy(|c| c != '"').parse_state(input));
let mut back_slash_char = satisfy(|c| "\"\nrt".chars().find(|x| *x == c).is_some()).map(|c| {
match c {
'"' => '"',
'\' => '\',
'n' => '\n',
'r' => '\r',
't' => '\t',
c => c//Should never happen
}
});
match c {
'\' => input.combine(|input| back_slash_char.parse_state(input)),
_ => Ok((c, input))
}
}
由于此解析器可能会消耗 1 或 2 个字符,因此使用原始组合器是不够的,因此我们需要引入一个函数,该函数可以在被解析的字符上分支。
我 运行 遇到了同样的问题,最终得到以下解决方案:
(
char('"'),
many1::<Vec<char>, _>(choice((
escaped_character(),
satisfy(|c| c != '"'),
))),
char('"')
)
或者换句话说,字符串由 "
后跟 many
escaped_characters
或任何不是结束符 "
的内容分隔,并由结束语 "
.
这是我如何使用它的完整示例:
pub enum Operand {
String { value: String },
}
fn escaped_character<I>() -> impl Parser<Input = I, Output = char>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
char('\'),
any(),
).and_then(|(_, x)| match x {
'0' => Ok('[=11=]'),
'n' => Ok('\n'),
'\' => Ok('\'),
'"' => Ok('"'),
_ => Err(StreamErrorFor::<I>::unexpected_message(format!("Invalid escape sequence \{}", x)))
})
}
#[test]
fn parse_escaped_character() {
let expected = Ok(('\n', " foo"));
assert_eq!(expected, escaped_character().easy_parse("\n foo"))
}
fn string_operand<I>() -> impl Parser<Input = I, Output = Operand>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
char('"'),
many1::<Vec<char>, _>(choice((
escaped_character(),
satisfy(|c| c != '"'),
))),
char('"')
)
.map(|(_,value,_)| Operand::String { value: value.into_iter().collect() })
}
#[test]
fn parse_string_operand() {
let expected = Ok((Operand::String { value: "foo \" bar \n baz [=11=]".into() }, ""));
assert_eq!(expected, string_operand().easy_parse(r#""foo \" bar \n baz [=11=]""#))
}