13

リストが1つ以上の空白行で区切られている改行区切りの単語リストを読み取るScalaパーサーコンビネーター文法を書いています。次の文字列を指定します。

cat
mouse
horse

apple
orange
pear

返してもらいたいList(List(cat, mouse, horse), List(apple, orange, pear))です。

単語リストを改行で区切られた単語として扱うこの基本的な文法を書きました。のデフォルト定義をオーバーライドする必要があったことに注意してくださいwhitespace

import util.parsing.combinator.RegexParsers

object WordList extends RegexParsers {

    private val eol = sys.props("line.separator")

    override val whiteSpace = """[ \t]+""".r

    val list: Parser[List[String]] = repsep( """\w+""".r, eol)

    val lists: Parser[List[List[String]]] = repsep(list, eol)

    def main(args: Array[String]) {
        val s =
          """cat
            |mouse
            |horse
            |
            |apple
            |orange
            |pear""".stripMargin

        println(parseAll(lists, s))
    }
}

これは空白行を空の単語リストとして誤って扱います。

[8.1] parsed: List(List(cat, mouse, horse), List(), List(apple, orange, pear))

(真ん中の空のリストに注意してください。)

各リストの最後に行末をオプションで付けることができます。

val list: Parser[List[String]] = repsep( """\w+""".r, eol) <~ opt(eol)

これは、リスト間に単一の空白行がある場合を処理しますが、複数の空白行でも同じ問題があります。

lists複数の行末区切り文字を許可するように定義を変更してみました。

val lists:Parser[List[List[String]]] = repsep(list, rep(eol))

しかし、これは上記の入力にかかっています。

複数の空白行を区切り文字として処理する正しい文法は何ですか?

4

1 に答える 1

14

空白の定義を再定義する代わりに、に設定skipWhitespaceしてみてください。false空のリストで発生している問題repsepは、リストの最後にある改行を消費しないという事実が原因です。代わりに、各項目の後に改行 (または場合によっては入力の終わり) を解析する必要があります。

import util.parsing.combinator.RegexParsers

object WordList extends RegexParsers {

  private val eoi = """\z""".r // end of input
  private val eol = sys.props("line.separator")
  private val separator = eoi | eol
  private val word = """\w+""".r

  override val skipWhitespace = false

  val list: Parser[List[String]] = rep(word <~ separator)

  val lists: Parser[List[List[String]]] = repsep(list, rep1(eol))

  def main(args: Array[String]) {
    val s =
      """cat
        |mouse
        |horse
        |
        |apple
        |orange
        |pear""".stripMargin

    println(parseAll(lists, s))
  }

}

繰り返しますが、パーサー コンビネータは、ここでは少しやり過ぎです。もっと単純なもので、実質的に同じことを得ることができます (ただし、リストの代わりに配列を使用します)。

s.split("\n{2,}").map(_.split("\n"))
于 2012-11-13T04:32:01.043 に答える