我想在
Scala中读取CSV字符串/文件,以便给定一个case类C和一个错误类型Error,解析器填充一个Iterable [Either [Error,C]].有没有图书馆做这个或类似的东西?
例如,给定类和错误
case class Person(name: String,age: Int) type Error = String
和CSV字符串
Foo,19 Ro Bar,24
解析器将输出
Stream(Right(Person("Foo",1)),Left("Cannot read 'Ro'"),Right(Person("Bar",24)))
更新:
我认为我的问题不清楚,所以让我澄清一下:在Scala中有没有定义样板?给定任何案例类,有没有办法自动加载?我想以这种方式使用它:
val iter = csvParserFor[Person].parseLines(lines)
解决方法
这是一个无懈可击的实现,与
your proposed example中的方法略有不同.这是基于我过去写过的一些代码,与实现的主要区别在于这一点更通用 – 例如实际的CSV解析部分被分解出来,因此很容易使用专用的库.
首先为一个多用途的读类型类(no Shapeless yet):
import scala.util.{ Failure,Success,Try } trait Read[A] { def reads(s: String): Try[A] } object Read { def apply[A](implicit readA: Read[A]): Read[A] = readA implicit object stringRead extends Read[String] { def reads(s: String): Try[String] = Success(s) } implicit object intRead extends Read[Int] { def reads(s: String) = Try(s.toInt) } // And so on... }
然后为有趣的部分:一个类型类,提供从列表的字符串转换(可能会失败)到HList:
import shapeless._ trait Fromrow[L <: HList] { def apply(row: List[String]): Try[L] } object Fromrow { import HList.ListCompat._ def apply[L <: HList](implicit fromrow: Fromrow[L]): Fromrow[L] = fromrow def fromFunc[L <: HList](f: List[String] => Try[L]) = new Fromrow[L] { def apply(row: List[String]) = f(row) } implicit val hnilFromrow: Fromrow[HNil] = fromFunc { case Nil => Success(HNil) case _ => Failure(new RuntimeException("No more rows expected")) } implicit def hconsFromrow[H: Read,T <: HList: Fromrow]: Fromrow[H :: T] = fromFunc { case h :: t => for { hv <- Read[H].reads(h) tv <- Fromrow[T].apply(t) } yield hv :: tv case Nil => Failure(new RuntimeException("Expected more cells")) } }
最后让它与案例类一起工作:
trait RowParser[A] { def apply[L <: HList](row: List[String])(implicit gen: Generic.Aux[A,L],fromrow: Fromrow[L] ): Try[A] = fromrow(row).map(gen. from) } def rowParserFor[A] = new RowParser[A] {}
现在我们可以写下面的例子,例如使用OpenCSV:
case class Foo(s: String,i: Int) import au.com.bytecode.opencsv._ import scala.collection.JavaConverters._ val reader = new CSVReader(new java.io.FileReader("foos.csv")) val foos = reader.readAll.asScala.map(row => rowParserFor[Foo](row.toList))
first,10 second,11 third,twelve
我们会得到以下内容:
scala> foos.foreach(println) Success(Foo(first,10)) Success(Foo(second,11)) Failure(java.lang.NumberFormatException: For input string: "twelve")