Idiomatische Methode zum Konvertieren eines InputStream in einen String in Scala

110

Ich habe eine praktische Funktion, die ich in Java zum Konvertieren eines InputStream in einen String verwendet habe. Hier ist eine direkte Übersetzung zu Scala:

  def inputStreamToString(is: InputStream) = {
    val rd: BufferedReader = new BufferedReader(new InputStreamReader(is, "UTF-8")) 
    val builder = new StringBuilder()    
    try {
      var line = rd.readLine 
      while (line != null) { 
        builder.append(line + "\n")
        line = rd.readLine
      }
    } finally {
      rd.close
    }
    builder.toString
  }

Gibt es eine idiomatische Möglichkeit, dies in Scala zu tun?

bballant
quelle

Antworten:

196

Für Scala> = 2.11

scala.io.Source.fromInputStream(is).mkString

Für Scala <2.11:

scala.io.Source.fromInputStream(is).getLines().mkString("\n")

macht so ziemlich das Gleiche. Ich bin mir nicht sicher, warum Sie Linien erhalten und sie dann alle wieder zusammenkleben möchten. Wenn Sie davon ausgehen können, dass der Stream nicht blockiert, können Sie einfach .availabledas Ganze in ein Byte-Array einlesen und daraus direkt eine Zeichenfolge erstellen.

Rex Kerr
quelle
2
Ein möglicher Grund, den ich selbst verwendet habe, ist die Normalisierung von Zeilenenden auf verschiedenen Betriebssystemen.
Kevin Wright
Raams Antwort ist auch fantastisch (und etwas prägnanter), aber Rex als DIE Antwort zu markieren, weil es spezifischer wie das Beispiel ist. Das Zusammenkleben der Linien war in einigen Fällen spezifisch, aber Sie haben mich daran erinnert, dass ich diesen Code an Stellen verwendet habe, an denen dies nicht ganz angemessen ist.
Bballant
Die Lösung ist nicht sehr sicher, da sie getLines () verwendet. Was ist, wenn der Eingabestream keine "Zeilenumbruch" -Zeichen enthält? dann blockiert das Ganze
Paul Sabou
Eine ziemlich schlechte Lösung. Was ist, wenn der Eingabestream DOS-Zeilenenden enthält (\ r \ n)? Diese würden durch diese Methode entfernt. Obwohl mkString einen Puffer verwendet, wäre es mit Sicherheit schneller, Zeichenblöcke zu lesen.
Dibbeke
1
@RexKerr Können Sie bitte auf den "Leistungsfehler" hinweisen, den Sie in Ihrer Antwort erwähnt haben? Ich habe beide Versionen mit einigen grundlegenden Testfällen getestet und bin auf kein Problem gestoßen.
Sahil Sareen
74

Source.fromInputStream(is).mkString("") wird auch die Tat tun .....

raam
quelle
Guter Punkt; Quelle schafft etwas, das sich ausdehnt Iterator[Char].
Rex Kerr
8
Es ist im Allgemeinen eine gute Praxis, bei solchen Vorgängen auch die Zeichenkodierung anzugeben. Zu diesem Zweck: Source.fromInputStream(is)(Codec.UTF8).mkString
Connor Doyle
Dies ist knapp, aber es schließt den Stream nicht, wohingegen der ursprüngliche Java-Code dies tat.
Rich
@Rich fromInputStream()scheint den Stream zu schließen, zumindest in Scala 2.11.
jaco0646
2
@ jaco0646 - Der Stream wird nicht geschlossen. Ich habe gerade getestet. Hier ist der Demo-Code, der dies beweist: gist.github.com/RichardBradley/bcd1a5e61fcc83e4e59f8b9b0bc2301c
Rich
13

Schneller geht das:

    private def inputStreamToString(is: InputStream) = {
        val inputStreamReader = new InputStreamReader(is)
        val bufferedReader = new BufferedReader(inputStreamReader)
        Iterator continually bufferedReader.readLine takeWhile (_ != null) mkString
    }
Kamil Lelonek
quelle
"schneller"? Aber es gab mir die Antwort, wie es geht, wenn ich nur ein Readerund kein habe InputStream.
BeepDog
3
Überspringen Sie einfach die erste Zeile und gehen Sie inputStreamReaderzur Methode über.
Kamil Lelonek
1
Dies ist möglicherweise eine Größenordnung schneller als scala.io.Source in Scala 2.11.7. Ich habe einen wirklich einfachen Benchmark davon geschrieben und meistens war er für große Dateien etwa 5% schneller (Test war ~ 35 MB Textdatei) bis zu 2.800% schneller für kleine Dateien (Test war ~ 30 KB). .
Colin Dean
2
Wunderschönen. Ich habe um eine elegante Lösung gekämpft und große Eingaben von gelesen Runtime.exec(). Das nagelt es.
Pavel Lechev
Wie würde ich einen zu verwendenden Zeichensatz angeben?
Wheeler