在本问题中,CSV样式引用的字符串是一个字符串,其中:
字符串的开头和结尾恰好是一个双引号,字符串中的双引号被折叠为一个双引号。( "
.
"Alo""ha"
→Alo"ha
."A""" e"
。这是一个A"
,后面跟着垃圾e"
.我尝试了几种方法,但没有一种是完全有效的。
多亏了Mozilla IRC上#nom中的用户pinkieval的一些帮助,我得到了最接近的帮助:
use std::error as stderror; /* Avoids needing nightly to compile */
named!(csv_style_string<&str, String>, map_res!(
terminated!(tag!("\""), not!(peek!(char!('"')))),
csv_string_to_string
));
fn csv_string_to_string(s: &str) -> Result<String, Box<stderror::Error>> {
Ok(s.to_string().replace("\"\"", "\""))
}
这不能正确地捕捉到字符串的结尾。
我还尝试将re_match!
宏与r#""([^"]|"")*""#
一起使用,但结果总是Err::Incomplete(1)
。
我已经确定given CSV example for Nom 1.0不适用于我所描述的带引号的CSV字符串,但我知道实现方式有所不同。
发布于 2018-06-09 23:17:28
这里有一种方法:
use nom::types::CompleteStr;
use nom::*;
named!(csv_style_string<CompleteStr, String>,
delimited!(
char!('"'),
map!(
many0!(
alt!(
// Eat a " delimiter and the " that follows it
tag!("\"\"") => { |_| '"' }
| // Normal character
none_of!("\"")
)
),
// Make a string from a vector of chars
|v| v.iter().collect::<String>()
),
char!('"')
)
);
fn main() {
println!(r#""Alo\"ha" = {:?}"#, csv_style_string(CompleteStr(r#""Alo""ha""#)));
println!(r#""" = {:?}"#, csv_style_string(CompleteStr(r#""""#)));
println!(r#"bad format: {:?}"#, csv_style_string(CompleteStr(r#""A""" e""#)));
}
(我是用全名编写的,但是像您这样的解决方案,基于外部函数而不是map!()
每个字符,也可以工作,而且效率可能更高。)
这里的神奇之处在于使用CompleteStr,这也可以解决您的正则表达式问题。这基本上告诉nom
在该输入之后不会出现任何内容(否则,nom
会假设您正在进行流解析器,因此可能会有更多的输入)。
这是必要的,因为我们需要知道如果"
是提供给nom
的最后一个字符,该如何处理它。根据它后面的字符(另一个"
、普通字符或EOF),我们必须做出不同的决定--因此产生了Incomplete
结果,这意味着nom
没有足够的输入来做出决定。告诉nom
接下来是EOF解决了这种犹豫不决的问题。
在nom
的作者博客:http://unhandledexpression.com/general/2018/05/14/nom-4-0-faster-safer-simpler-parsers.html#dealing-with-incomplete-usage上进一步阅读Incomplete
您可能会注意到,此解析器实际上并不拒绝无效的输入,而是解析开头并返回其余部分。如果您使用这个解析器作为另一个解析器中的子解析器,那么后者将把剩余的部分提供给下一个子解析器,下一个子解析器也会崩溃(因为它会期望一个逗号),从而导致整个解析器失败。
如果你不想这样,你可以让csv_style_string
匹配peek!(alt!(char!(',')|char!('\n")|eof!()))
。
https://stackoverflow.com/questions/50740577
复制