如何匹配 nom 中的 CSV 样式带引号的字符串?
How do I match a CSV-style quoted string in nom?
CSV 样式引用字符串,对于这个问题的目的,是一个字符串,其中:
- 字符串的开头和结尾正好是一个
"
。
- 字符串中的两个双引号折叠为一个双引号。
"Alo""ha"
→Alo"ha
.
- "" 本身是一个空字符串。
- 无法解析错误输入,例如
"A""" e"
。这是一个 A"
,然后是垃圾 e"
。
我已经尝试了几种方法,none 其中完全有效。
我得到的最接近的结果,感谢 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 字符串,但我知道实现方式不同。
这是一种方法:
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
作者博客上的 Incomplete
:http://unhandledexpression.com/general/2018/05/14/nom-4-0-faster-safer-simpler-parsers.html#dealing-with-incomplete-usage
您可能会注意到此解析器实际上并没有拒绝无效输入,而是解析开头部分和 returns 其余部分。如果您将此解析器用作另一个解析器中的子解析器,后者会将剩余部分提供给下一个子解析器,这也会崩溃(因为它需要一个逗号),导致整个解析器失败。
如果您不想这样,您可以使 csv_style_string
匹配 peek!(alt!(char!(',')|char!('\n")|eof!()))
。
CSV 样式引用字符串,对于这个问题的目的,是一个字符串,其中:
- 字符串的开头和结尾正好是一个
"
。 - 字符串中的两个双引号折叠为一个双引号。
"Alo""ha"
→Alo"ha
. - "" 本身是一个空字符串。
- 无法解析错误输入,例如
"A""" e"
。这是一个A"
,然后是垃圾e"
。
我已经尝试了几种方法,none 其中完全有效。
我得到的最接近的结果,感谢 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 字符串,但我知道实现方式不同。
这是一种方法:
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
作者博客上的 Incomplete
:http://unhandledexpression.com/general/2018/05/14/nom-4-0-faster-safer-simpler-parsers.html#dealing-with-incomplete-usage
您可能会注意到此解析器实际上并没有拒绝无效输入,而是解析开头部分和 returns 其余部分。如果您将此解析器用作另一个解析器中的子解析器,后者会将剩余部分提供给下一个子解析器,这也会崩溃(因为它需要一个逗号),导致整个解析器失败。
如果您不想这样,您可以使 csv_style_string
匹配 peek!(alt!(char!(',')|char!('\n")|eof!()))
。