обработать CSV в Scala - PullRequest
       3

обработать CSV в Scala

4 голосов
/ 13 июня 2010

Я использую scala 2.7.7 и хотел проанализировать файл CSV и сохранить данные в базе данных SQLite.

В итоге я использовал библиотеку Java OpenCSV для анализа файла CSV и библиотеку sqlitejdbc.

Использование этих библиотек Java делает мой код Scala почти идентичным коду Java (без точки с запятой и с val / var)

Поскольку я имею дело с java-объектами, я не могу использовать список scala, карту и т. Д., Если не произвожу преобразование 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 ]

9 голосов
/ 13 июня 2010

Если у вас есть простой 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...

Преимущество в том, что вы можете использовать полный API-интерфейс Scala Collection.

Если вы предпочитаете использовать комбинаторы синтаксического анализа, есть также пример комбинатора синтаксического анализа на github.

3 голосов
/ 12 июня 2012

Если вы хотите разобрать его в 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 и т. Д., Но, похоже, оно работает для CSV ASCII, которые имеют как минимум кавычки.

3 голосов
/ 13 июня 2010

Инструкция 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). Но использовать время хорошо. Можно также изменить рефакторинг, чтобы избежать lastSymbol (например, с помощью рекурсивного определения), но я не уверен, что оно того стоит.

2 голосов
/ 19 декабря 2015

Если вы хотите что-то более идиоматичное и более безопасное для типов, могу я предложить kantan.csv ?

Позволяет превратить любой источник данных CSV в коллекцию хорошо типизированных значений. Чтобы переписать CSV-часть вашего примера (и иметь дело с датами в виде строк, потому что я не знаю, в каком формате вы их получаете), вы должны написать:

import kantan.csv.ops._

type Row = (String, String, Double, Double, Double, Double, Double)

// Do whatever it is you need to do with each row
def sqliteMagic(row: Row): Unit = ???

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

Обратите внимание, что я не особенно люблю использовать кортежи, когда вы можете использовать более конкретные типы. Используя shapeless модуль kantan.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)

Обратите внимание, что вам не пришлось выполнять какую-либо работу для поддержки класса case Symbol, который довольно приятен и все еще безопасен при использовании типа благодаря бесформенному.

Полное раскрытие: я автор kantan.csv.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...