加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

将Scala中的CSV读入具有错误处理的类实例

发布时间:2020-12-16 19:01:21 所属栏目:安全 来源:网络整理
导读:我想在 Scala中读取CSV字符串/文件,以便给定一个case类C和一个错误类型Error,解析器填充一个Iterable [Either [Error,C]].有没有图书馆做这个或类似的东西? 例如,给定类和错误 case class Person(name: String,age: Int)type Error = String 和CSV字符串 Fo
我想在 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")

(请注意,这引发了每一行的Generic和FromRow实例,但是如果性能是一个问题,那么很容易改变).

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读