2015-06-10 4 views
6

Ich muss eine große Datei in Scala lesen und in Blöcken von k Bits verarbeiten (k könnte 65536 typischerweise sein). Als einfaches Beispiel (aber nicht was ich will):Lesen sehr großer Dateien (~ 1 TB) in sequentiellen Blöcken

Dateiblöcke sind (f1, f2, ... fk).

Ich möchte SHA256(f1)+SHA256(f2)+...+ SHA256(fk)

Eine solche Berechnung berechnen kann, ohne dass andere Blöcke inkrementell nur mit konstanter Lagerung und den aktuellen Block durchgeführt werden.

Wie liest man die Datei am besten? (vielleicht etwas, das Fortsetzungen verwendet?)

EDIT: Die verknüpfte Frage Art löst das Problem, aber nicht immer, da die Datei, die ich betrachte binäre Daten enthält.

+0

@Christian, Nein, das nicht von der Frage zitiert Duplikat ist. – Biswanath

+0

Ich kann nicht verstehen, wie dies ein Duplikat mit der zitierten Frage ist. Die andere Frage bezieht sich auf eine textbasierte CSV-Datei, diese Frage beschäftigt sich mit einer "nicht textbasierten CSV" -Datei. Die Antworten der anderen Frage sollten nicht gelten. Ich zweifle wirklich daran, wer das jemals als Duplikat markiert hat, wenn sie beide Fragen vollständig lesen. – Biswanath

Antwort

4

Hier ist ein Ansatz mit Akka Streams. Dies verwendet konstanten Speicher und kann die Dateiblöcke so verarbeiten, wie sie gelesen werden.

Weitere Informationen finden Sie unter "Streaming File IO" am Ende dieser Seite. http://doc.akka.io/docs/akka-stream-and-http-experimental/1.0-RC3/scala/stream-io.html

Beginnen Sie mit einer einfachen build.sbt Datei:

scalaVersion := "2.11.6" 

libraryDependencies ++= Seq(
     "com.typesafe.akka" %% "akka-stream-experimental" % "1.0-RC3" 
) 

Die interessanten Teile sind die Source, Flow und Sink. Die Source ist eine SynchronousFileSource, die eine große Datei mit einer Chunk-Größe von 65536 einliest. Ein ByteString der Chunk-Größe wird von Source ausgegeben und von einem Flow verbraucht, der einen SHA256-Hash für jeden Chunk berechnet. Zuletzt nimmt die Sink die Ausgabe von Flow und druckt die Byte-Arrays aus. Sie werden diese konvertieren und sie mit einem fold summieren, um eine Gesamtsumme zu erhalten.

import akka.stream.io._ 
import java.io.File 
import scala.concurrent.Future 
import akka.stream.scaladsl._ 
import akka.actor.ActorSystem 
import akka.stream.ActorFlowMaterializer 
import java.security.MessageDigest 

object LargeFile extends App{ 
    implicit val system = ActorSystem("Sys") 
    import system.dispatcher 
    implicit val materializer = ActorFlowMaterializer() 

    val file = new File("<path to large file>") 

    val fileSource = SynchronousFileSource(file, 65536) 

    val shaFlow = fileSource.map(chunk => sha256(chunk.toString)) 

    shaFlow.to(Sink.foreach(println(_))).run//TODO - Convert the byte[] and sum them using fold 

    def sha256(s: String) = { 
    val messageDigest = MessageDigest.getInstance("SHA-256") 
    messageDigest.digest(s.getBytes("UTF-8")) 
    } 
} 

BYTE ARRAYS!

> run 
[info] Running LargeFile 
[[email protected] 
[[email protected] 
[[email protected] 
... 
0

den Digest Erstellen der Strom ständig mit, die ich glaube, erzeugt ein Iterator

import java.File 
import java.FileInputStream 
import java.security.MessageDigest 

val file = new File("test.in") 
val is = new FileInputStream(file) 

val md = MessageDigest.getInstance("SHA-256") 

val bytes = Array.fill[Byte](65536)(0) 

Stream 
    .continually((is.read(bytes),bytes)) 
    .takeWhile(_._1 != -1) 
    .foreach{ x => md.update(x._2,0,x._1) } 

println(md.digest()) 
// prinln(md.digest().map("%02X" format _).mkString) // if you want hex string 
+0

cala.collection.immutable.Stream ist memoisiert, was das ganze Ding in den Speicher lesen würde (laut http://stackoverflow.com/questions/4255021/how-do-i-read-a-large-csv-file- Mit-Scala-Stream-Klasse # Antwort-4255338) – mikebridge