【问题标题】:Using parboiled2 to parse multiple lines instead of a String使用 parboiled2 解析多行而不是字符串
【发布时间】:2015-01-11 16:21:56
【问题描述】:

我想使用 parboiled2 来解析多个 CSV 行而不是单个 CSV 字符串。结果会是这样的:

val parser = new CSVRecordParser(fieldSeparator)
io.Source.fromFile("my-file").getLines().map(line => parser.record.run(line))

其中 CSVRecordParser 是我的 CSV 记录半熟解析器。我遇到的问题是,对于我所尝试的,我不能这样做,因为煮熟的解析器需要在构造函数中输入,而不是在 run 方法中。因此,我可以为每一行创建一个新的解析器,这是不好的,或者找到一种方法将输入传递给解析器,用于我拥有的每个输入。我试图通过将输入设置为变量并将解析器包装在另一个对象中来破解解析器

object CSVRecordParser {

  private object CSVRecordParserWrapper extends Parser with StringBuilding {

    val textBase = CharPredicate.Printable -- '"'
    val qTextData = textBase ++ "\r\n"

    var input: ParserInput = _
    var fieldDelimiter: Char = _

    def record = rule { zeroOrMore(field).separatedBy(fieldDelimiter) ~> (Seq[String] _) }
    def field = rule { quotedField | unquotedField }
    def quotedField = rule {
      '"' ~ clearSB() ~ zeroOrMore((qTextData | '"' ~ '"') ~ appendSB()) ~ '"' ~ ows ~ push(sb.toString)
    }
    def unquotedField = rule { capture(zeroOrMore(textData)) }
    def textData = textBase -- fieldDelimiter

    def ows = rule { zeroOrMore(' ') }
  }

  def parse(input: ParserInput, fieldDelimiter: Char): Result[Seq[String]] = {
    CSVRecordParserWrapper.input = input
    CSVRecordParserWrapper.fieldDelimiter = fieldDelimiter
    wrapTry(CSVRecordParserWrapper.record.run())
  }
}

然后当我想解析一行时只需调用CSVRecordParser.parse(input, separator)。除了这很可怕之外,它不起作用,而且我经常遇到与解析器以前的用法有关的奇怪错误。我知道这不是我应该使用 parboiled2 编写解析器的方式,我想知道用这个库实现我想要做什么的最佳方式是什么。

【问题讨论】:

  • 为每一行创建一个新的解析器对象有什么问题?
  • 如果这个新对象只是一个现有对象的一个​​字段的克隆,并且对其他对象没有影响,那么为每一行创建一个对象似乎很昂贵。
  • @Gabor 是的,为每一行创建成本很高。这是一个很好的(恕我直言)问题。

标签: scala parsing csv parboiled


【解决方案1】:

为什么不向解析器添加记录结束规则。

def EOR = rule { "\r\n" | "\n" }

def record = rule { zeroOrMore(field).separatedBy(fieldDelimiter) ~ EOR ~> (Seq[String] _) }

然后你可以传入任意多的行。

【讨论】:

  • 聪明的想法 - 您是否尝试过并验证单个解析器能够处理多行?
【解决方案2】:

我在一个需要高速和低资源的项目中为超过 100 万条记录的 CSV 文件完成了这项工作,我发现为每一行实例化一个新的解析器效果很好。

在我注意到 parboiled2 自述文件提到解析器的重量非常轻后,我尝试了这种方法。

我什至不需要从它们的默认值中增加 JVM 内存或堆限制。每一行的解析器实例化效果都很好。

【讨论】:

  • 这对于我参与的各种项目不起作用 - 在这些项目中,即使创建新的字符串也必须保持在最低限度。恕我直言,原始的 OP 在这里没有得到回答:我会寻找一个应用于输入的 AST。不是每次都重新解析和生成新的解析器。例如,数十秒内需要 2.5 亿次 AST 评估。我的手写解析器能够处理它..但我更喜欢煮熟的或类似的解决方案。
  • 我很想看到一个涵盖您的用例的答案,因为系统如此拥挤以至于必须最小化字符串的创建。我构建的系统每天在股票 java 7 平台上处理数亿条记录。我们密切关注资源使用情况,发现它在边界内不间断地运行,24/7。如果文档没有提示,我永远不会尝试以这种方式实现它,但它确实做到了,而且奏效了,我很惊讶。
猜你喜欢
  • 2018-02-15
  • 1970-01-01
  • 1970-01-01
  • 2016-11-21
  • 2018-12-10
  • 2014-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多