在scala中处理csv

时间:2010-06-13 04:20:49

标签: java sqlite scala

我正在使用scala 2.7.7,并希望解析CSV文件并将数据存储在SQLite数据库中。

我最终使用OpenCSV java库来解析CSV文件,并使用sqlitejdbc库。

使用这些java库使我的scala代码看起来几乎与Java代码相同(没有分号和val / var)

当我处理java对象时,我不能使用scala list,map等,除非我做scala2java转换或升级到scala 2.8

有没有办法可以使用我不知道的scala位进一步简化代码?

val filename = "file.csv";
val reader = new CSVReader(new FileReader(filename))
var aLine = new Array[String](10)
var lastSymbol = ""
while( (aLine = reader.readNext()) != null ) {
    if( aLine != null ) {
        val symbol = aLine(0)
        if( !symbol.equals(lastSymbol)) { 
            try {
                val rs = stat.executeQuery("select name from sqlite_master where name='" + symbol + "';" )
                if( !rs.next() ) {
                    stat.executeUpdate("drop table if exists '" + symbol + "';")
                    stat.executeUpdate("create table '" + symbol + "' (symbol,data,open,high,low,close,vol);")
                }
            }
            catch {
              case sqle : java.sql.SQLException =>
                 println(sqle)

            }
            lastSymbol = symbol
        }
        val prep = conn.prepareStatement("insert into '" + symbol + "' values (?,?,?,?,?,?,?);")
        prep.setString(1, aLine(0)) //symbol
        prep.setString(2, aLine(1)) //date
        prep.setString(3, aLine(2)) //open
        prep.setString(4, aLine(3)) //high
        prep.setString(5, aLine(4)) //low
        prep.setString(6, aLine(5)) //close
        prep.setString(7, aLine(6)) //vol
        prep.addBatch()
        prep.executeBatch()
     }
}
conn.close()

4 个答案:

答案 0 :(得分:9)

如果您有一个简单的CSV文件,则可以选择根本不使用任何CSV库,只需在Scala中解析它,例如:


case class Stock(line: String) {
  val data = line.split(",")
  val date = data(0)
  val open = data(1).toDouble
  val high = data(2).toDouble
  val low = data(3).toDouble
  val close = data(4).toDouble
  val volume = data(5).toDouble
  val adjClose = data(6).toDouble

  def price: Double = low
}

scala> import scala.io._

scala> Source.fromFile("stock.csv") getLines() map (l => Stock(l))
res0: Iterator[Stock] = non-empty iterator


scala> res0.toSeq  
res1: Seq[Stock] = List(Stock(2010-03-15,37.90,38.04,37.42,37.64,941500,37.64), Stock(2010-03-12,38.00,38.08,37.66,37.89,834800,37.89) //etc...

这样可以使用完整的Scala集合API。

如果您更喜欢使用解析器组合器,那么github上还有一个csv parser combinator的示例。

答案 1 :(得分:3)

if之后的while语句无效 - 您已确保aLine不为空。

另外,我不确切知道aLine的内容到底是什么,但你可能想做类似的事情

aLine.zipWithIndex.foreach(i => prep.setString(i._2+1 , i._1))

而不是从1到7手动计数。或者,你可以

for (i <- 1 to 7) { prep.setString(i, aLine(i)) }

如果您觉得采用更具功能性的风格,可以用

代替while
Iterator.continually(reader.readNext()).takeWhile(_!=null).foreach(aLine => {
  // Body of while goes here
}

(并删除var aLine)。但使用while可以。也可以重构以避免lastSymbol(例如通过使用递归def),但我不确定这是值得的。

答案 2 :(得分:3)

如果你想在Scala中解析它,内置的解析器是非常强大的,一旦你掌握它,很容易。我不是专家,但通过一些规范测试,证明这是有用的:

object CSVParser extends RegexParsers {
  def apply(f: java.io.File): Iterator[List[String]] = io.Source.fromFile(f).getLines().map(apply(_))
  def apply(s: String): List[String] = parseAll(fromCsv, s) match {
    case Success(result, _) => result
    case failure: NoSuccess => {throw new Exception("Parse Failed")}
  }

  def fromCsv:Parser[List[String]] = rep1(mainToken) ^^ {case x => x}
  def mainToken = (doubleQuotedTerm | singleQuotedTerm | unquotedTerm) <~ ",?".r ^^ {case a => a}
  def doubleQuotedTerm: Parser[String] = "\"" ~> "[^\"]+".r <~ "\"" ^^ {case a => (""/:a)(_+_)}
  def singleQuotedTerm = "'" ~> "[^']+".r <~ "'" ^^ {case a => (""/:a)(_+_)}
  def unquotedTerm = "[^,]+".r ^^ {case a => (""/:a)(_+_)}

  override def skipWhitespace = false
}

这可能不是我认为的功能完整解决方案,我不是它如何处理UTF-8等,但它似乎适用于至少有引号的ASCII CSV。

答案 3 :(得分:2)

如果你想要一些更具惯用性且更安全的东西,我可以建议kantan.csv吗?

它允许您将任何CSV数据源转换为良好类型值的集合。要重写示例的CSV解析部分(并将日期作为字符串处理,因为我不知道你收到它们的格式),你要写:

import kantan.csv.ops._
import kantan.csv.generic.codecs._

case class Symbol(name: String, date: String, open: Double, high: Double, low: Double, close: Double, vol: Double)

def sqliteMagic(symbol: Symbol): Unit = ???

new File(filename).asUnsafeCsvRows[Symbol](',', false).foreach(sqliteMagic)

请注意,当您可以使用更具体的类型时,我并不特别喜欢使用元组。使用kantan.csv的shapeless模块,您可以更好地编写它:

Symbol

请注意,您不必为支持var案例类做任何工作,这非常好,并且由于无形而仍然是类型安全的。

完全披露:我是kantan.csv的作者。