用于解析转义的CSV行的Parboiled2语法

时间:2015-07-16 15:26:26

标签: scala parsing csv peg parboiled2

我试图将包含由分隔符分隔的字符串的单行解析为这些字符串的序列。它应该能够在字符串中包含任何字符,如果字段包含分隔符,则它需要围绕它的双引号。为了在这样的字段中使用双引号,双引号将被转义。

我以此为出发点:https://github.com/sirthias/parboiled2/blob/695ee6603359cfcb97734edf6dd1d27383c48727/examples/src/main/scala/org/parboiled2/examples/CsvParser.scala

我的语法看起来像这样:

class CsvParser(val input: ParserInput, val delimiter: String = ",") extends Parser {
  def line: Rule1[Seq[String]] = rule {record ~ EOI}
  def record = rule(oneOrMore(field).separatedBy(delimiter))

  def QUOTE = "\""
  def ESCAPED_QUOTE = "\\\""
  def DELIMITER_QUOTE = delimiter+"\""
  def WS = " \t".replace(delimiter, "")

  def field = rule{whiteSpace ~ ((QUOTE ~ escapedField ~ QUOTE) | unquotedField) ~ whiteSpace}
  def escapedField = rule { capture(zeroOrMore(noneOf(QUOTE) | ESCAPED_QUOTE)) ~> (_.replace(ESCAPED_QUOTE, QUOTE))  } 
  def unquotedField = rule { capture(zeroOrMore(noneOf(DELIMITER_QUOTE))) }
  def whiteSpace = rule(zeroOrMore(anyOf(WS)))
}

当我用"quote\"key",1,2打电话时 我得到Invalid input 'k', expected whiteSpace, ',' or 'EOI' (line 1, column 9)

我做错了什么?我该怎么调试呢? (作为一个额外的问题:我如何扩展语法以允许分隔符为多个字符,如##?)

谢谢!

1 个答案:

答案 0 :(得分:2)

Parboiled2似乎在没有回溯的情况下执行规则。

在这个特殊情况下

def escapedField = rule { capture(zeroOrMore(noneOf(QUOTE) | ESCAPED_QUOTE)) ~> (_.replace(ESCAPED_QUOTE, QUOTE))  } 

noneOf(QUOTE)从\"中捕获\然后返回,而不是回溯并尝试捕获完整的\"。

使用

解决了错误
def escapedField = rule { capture(ESCAPED_QUOTE | zeroOrMore(noneOf(QUOTE))) ~> (_.replace(ESCAPED_QUOTE, QUOTE))  }