Idiomatyczny sposób konwertowania InputStream na String w Scali

111

Mam przydatną funkcję, której używałem w Javie do konwersji InputStream na String. Oto bezpośrednie tłumaczenie na 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
  }

Czy istnieje idiomatyczny sposób na zrobienie tego w scali?

bballant
źródło

Odpowiedzi:

197

Dla Scala> = 2.11

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

Dla Scala <2.11:

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

robi prawie to samo. Nie jestem jednak pewien, dlaczego chcesz uzyskać linie, a następnie skleić je wszystkie razem. Jeśli możesz założyć, że strumień jest nieblokujący, możesz po prostu użyć .available, wczytać całość do tablicy bajtów i bezpośrednio utworzyć łańcuch.

Rex Kerr
źródło
2
Jednym z możliwych powodów, z których sam korzystałem, jest normalizacja zakończeń linii w różnych systemach operacyjnych.
Kevin Wright,
Odpowiedź Raama jest również niesamowita (i nieco bardziej zwięzła), ale oznaczając Rexa jako odpowiedź, ponieważ jest bardziej podobna do przykładu. Sklejanie linii z powrotem było specyficzne w kilku przypadkach, ale przypomniałeś mi, że użyłem tego kodu w miejscach, w których nie jest to właściwe.
bballant
rozwiązanie nie jest zbyt bezpieczne, ponieważ używa getLines (); co się stanie, jeśli strumień wejściowy nie zawiera znaków „nowej linii”? potem wszystko się blokuje
Paul Sabou
Całkiem złe rozwiązanie. A co jeśli strumień wejściowy zawiera zakończenia linii DOS (\ r \ n). Te zostałyby usunięte tą metodą. Ponadto, chociaż mkString używa bufora, z pewnością szybsze byłoby odczytywanie bloków znaków.
Dibbeke
1
@RexKerr Czy możesz wskazać "błąd wydajności", o którym wspomniałeś w swojej odpowiedzi. Przetestowałem obie wersje z kilkoma podstawowymi testami i nie napotkałem żadnego problemu.
Sahil Sareen
74

Source.fromInputStream(is).mkString("") również zrobi czyn .....

raam
źródło
Słuszna uwaga; źródło tworzy coś, co się rozciąga Iterator[Char].
Rex Kerr,
8
Generalnie dobrą praktyką jest określanie kodowania znaków podczas wykonywania tego typu czynności. W tym celu: Source.fromInputStream(is)(Codec.UTF8).mkString
Connor Doyle,
Jest to zwięzłe, ale nie zamyka strumienia, w przeciwieństwie do oryginalnego kodu Java.
Rich
@Rich, fromInputStream()wydaje się zamykać strumień, przynajmniej w Scali 2.11.
jaco0646
2
@ jaco0646 - nie zamyka strumienia. Właśnie przetestowałem. Oto kod demo, który to potwierdza: gist.github.com/RichardBradley/bcd1a5e61fcc83e4e59f8b9b0bc2301c
Rich
13

Szybszy sposób na zrobienie tego:

    private def inputStreamToString(is: InputStream) = {
        val inputStreamReader = new InputStreamReader(is)
        val bufferedReader = new BufferedReader(inputStreamReader)
        Iterator continually bufferedReader.readLine takeWhile (_ != null) mkString
    }
Kamil Lelonek
źródło
"szybciej"? Ale dostarczył mi odpowiedzi, jak to zrobić, gdy mam tylko Readerplik InputStream.
BeepDog
3
Po prostu pomiń pierwszą linię i przejdź inputStreamReaderdo metody.
Kamil Lelonek
1
Jest to potencjalnie o rząd wielkości szybsze niż scala.io Źródło w Scala 2.11.7. Napisałem naprawdę podstawowy test porównawczy i przez większość czasu był około 5% szybszy dla dużych plików (test był ~ 35 MB plik tekstowy) aż do 2800% szybciej dla małych plików (test był ~ 30 KB) .
Colin Dean
2
Piękny. Zmagałem się z eleganckim rozwiązaniem odczytującym duże dane wejściowe Runtime.exec(). To gwoździ.
Pavel Lechev,
Jak określić zestaw znaków do użycia?
wheeler