首先:学分。此代码基于此处的解决方案:Use Scala parser combinator to parse CSV files
我要解析的CSV文件可以有注释,以#开头的行。并避免混淆:CSV文件以制表符分隔。有更多的约束条件会使解析器变得更容易,但由于我完全不熟悉Scala,我认为最好保持尽可能接近(工作)原文。
我遇到的问题是我的类型不匹配。显然,评论的正则表达式不会产生列表。我希望Scala将评论解释为1元素列表,但事实并非如此。
那么我如何修改我可以处理此注释行的代码呢?和closly相关:是否有一种优雅的方式来查询解析器结果,所以我可以在myfunc中写一些像
if (isComment(a)) continue
所以这是实际的代码:
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf
import scala.util.parsing.combinator._
object MyParser extends RegexParsers {
override val skipWhitespace = false // meaningful spaces in CSV
def COMMA = ","
def TAB = "\t"
def DQUOTE = "\""
def HASHTAG = "#"
def DQUOTE2 = "\"\"" ^^ { case _ => "\"" } // combine 2 dquotes into 1
def CRLF = "\r\n" | "\n"
def TXT = "[^\",\r\n]".r
def SPACES = "[ ]+".r
def file: Parser[List[List[String]]] = repsep((comment|record), CRLF) <~ (CRLF?)
def comment: Parser[List[String]] = HASHTAG<~TXT
def record: Parser[List[String]] = "[^#]".r<~repsep(field, TAB)
def field: Parser[String] = escaped|nonescaped
def escaped: Parser[String] = {
((SPACES?)~>DQUOTE~>((TXT|COMMA|CRLF|DQUOTE2)*)<~DQUOTE<~(SPACES?)) ^^ {
case ls => ls.mkString("")
}
}
def nonescaped: Parser[String] = (TXT*) ^^ { case ls => ls.mkString("") }
def applyParser(s: String) = parseAll(file, s) match {
case Success(res, _) => res
case e => throw new Exception(e.toString)
}
def myfunc( a: (String, String)) = {
val parserResult = applyParser(a._2)
println("APPLY PARSER FOR " + a._1)
for( a <- parserResult ){
a.foreach { println }
}
}
def main(args: Array[String]) {
val filesPath = "/home/user/test/*.txt"
val conf = new SparkConf().setAppName("Simple Application")
val sc = new SparkContext(conf)
val logData = sc.wholeTextFiles(filesPath).cache()
logData.foreach( x => myfunc(x))
}
}
答案 0 :(得分:1)
由于注释的解析器和记录的解析器是“or-ed”在一起,因此它们必须属于同一类型。
您需要进行以下更改:
def comment: Parser[List[String]] = HASHTAG<~TXT ^^^ {List()}
通过使用^^^
,我们将解析器的结果(由HASHTAG解析器返回的结果)转换为空列表。
也改变:
def record: Parser[List[String]] = repsep(field, TAB)
请注意,因为注释和记录解析器是or-ed并且因为注释首先出现,所以如果行以"#"
开头,它将由注释解析器解析。
编辑:
为了将注释文本保留为解析器的输出(如果您想稍后打印它们),并且因为您使用|
,您可以执行以下操作:
定义以下类:
trait Line
case class Comment(text: String) extends Line
case class Record(elements: List[String]) extends Line
现在定义评论,记录&amp;文件解析器如下:
val comment: Parser[Comment] = "#" ~> TXT ^^ Comment
val record :Parser[Line]= repsep(field, TAB) ^^ Record
val file: Parser[List[Line]] = repsep(comment | record, CRLF) <~ (CRLF?)
现在您可以定义打印功能myFunc
:
def myfunc( a: (String, String)) = {
parseAll(file, a._2).map { lines =>
lines.foreach{
case Comment(t) => println(s"This is a comment: $t")
case Record(elems) => println(s"This is a record: ${elems.mkString(",")}")
}
}
}