web-dev-qa-db-ja.com

Akkaストリームを使用したCSVファイルの読み取り

私はcsvファイルを読んでいます。これを行うためにAkka Streamsを使用して、各行で実行するアクションのグラフを作成できるようにしています。次のおもちゃのサンプルを実行しました。

  def main(args: Array[String]): Unit = {
    implicit val system = ActorSystem("MyAkkaSystem")
    implicit val materializer = ActorMaterializer()

        val source = akka.stream.scaladsl.Source.fromIterator(Source.fromFile("a.csv").getLines)
        val sink = Sink.foreach(println)
        source.runWith(sink)
      }

2つのSourceタイプは、私には簡単にはいかない。これは慣用的ですか、これを書くためのより良い方法はありますか?

11
M.K.

実際、akka-streamsはファイルから直接読み取る関数を提供します。

FileIO.fromPath(Paths.get("a.csv"))
      .via(Framing.delimiter(ByteString("\n"), 256, true).map(_.utf8String))
      .runForeach(println)

ここで、runForeachメソッドは行を出力することです。これらの行を処理するための適切なSinkがある場合は、この関数の代わりにそれを使用してください。たとえば、行を'で分割し、その中の単語の総数を出力する場合は、次のようにします。

val sink: Sink[String] = Sink.foreach(x => println(x.split(",").size))

FileIO.fromPath(Paths.get("a.csv"))
      .via(Framing.delimiter(ByteString("\n"), 256, true).map(_.utf8String))
      .to(sink)
      .run()
16
fcat

Akka StreamsでCSVファイルを読み取る慣用的な方法は、 Alpakka CSVコネクタ を使用することです。次の例では、CSVファイルを読み取り、それを列名(ファイルの最初の行と想定)とByteString値のマップに変換し、ByteString値をString値、および各行を出力します。

FileIO.fromPath(Paths.get("a.csv"))
  .via(CsvParsing.lineScanner())
  .via(CsvToMap.toMap())
  .map(_.mapValues(_.utf8String))
  .runForeach(println)
13
Jeffrey Chung

これを試して:

package ru.io

import Java.nio.file.Paths

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl._
import akka.util.ByteString

import scala.concurrent.Await
import scala.concurrent.duration._

object ReadStreamApp extends App {
  implicit val actorSystem = ActorSystem()
  import actorSystem.dispatcher
  implicit val flowMaterializer = ActorMaterializer()

  // читать строки из файла журнала
  val logFile = Paths.get("src/main/resources/a.csv")

  val source = FileIO.fromPath(logFile)

  // анализировать фрагменты байтов в строки
  val flow = Framing
    .delimiter(ByteString(System.lineSeparator()), maximumFrameLength = 512, allowTruncation = true)
    .map(_.utf8String)

  val sink = Sink.foreach(println)

  source
    .via(flow)
    .runWith(sink)
    .andThen {
      case _ =>
        actorSystem.terminate()
        Await.ready(actorSystem.whenTerminated, 1 minute)
    }
}
2

ええ、これらは異なるSourcesなので問題ありません。しかし、あなたが気に入らない場合はscala.io.Source自分でファイルを読み取って(ソースcsvファイルを圧縮する必要がある場合もあります)、次のように指定されたInputStreamを使用して解析できます

StreamConverters.fromInputStream(() => input)
  .via(Framing.delimiter(ByteString("\n"), 4096))
  .map(_.utf8String)
  .collect { line =>
    line
  }

とは言っても、Apache Commons CSV akka-streamを使用。あなたはより少ないコードを書くことになるかもしれません:)

2
expert