Идиоматический способ преобразования InputStream в String в Scala


111

У меня есть удобная функция, которую я использовал в Java для преобразования InputStream в String. Вот прямой перевод на 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
  }

Есть ли идиоматический способ сделать это в scala?

Ответы:


197

Для Scala> = 2.11

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

Для Scala <2.11:

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

делает примерно то же самое. Не знаю, почему вы хотите получить линии, а затем склеить их все вместе. Если вы можете предположить, что поток неблокирован, вы можете просто использовать .available, прочитать все это в массиве байтов и создать строку напрямую из него.


2
Одна из возможных причин, которую я использовал сам, - это нормализовать окончания строк в разных операционных системах.
Кевин Райт,

Ответ Раама также потрясающий (и немного более краткий), но он пометил Рекса как ИДЕАЛЬНЫЙ ответ, потому что он более точно похож на пример. Склеивание строк обратно было характерно для нескольких случаев, но вы напомнили мне, что я использовал этот код там, где это не совсем подходит.
bballant 07

решение не очень безопасно, поскольку оно использует getLines (); что, если во входном потоке нет символов «новой строки»? тогда все это блокируется
Пол Сабу

Довольно плохое решение. Что делать, если входной поток содержит окончания строк DOS (\ r \ n). Они будут удалены этим методом. Кроме того, хотя mkString использует буфер, наверняка будет быстрее читать блоки символов.
Dibbeke

1
@RexKerr Не могли бы вы указать на «ошибку производительности», которую вы упомянули в своем ответе. Я тестировал обе версии с некоторыми базовыми тестами и не обнаружил никаких проблем.
Сахил Сарин

74

Source.fromInputStream(is).mkString("") тоже сделает дело .....


Хорошая точка зрения; источник создает нечто расширяющееся Iterator[Char].
Рекс Керр

8
Как правило, при подобных действиях рекомендуется также указывать кодировку символов. С этой целью: Source.fromInputStream(is)(Codec.UTF8).mkString
Коннор Дойл,

Это кратко, но не закрывает поток, в отличие от исходного кода Java.
Rich

@Rich, fromInputStream()похоже, закрывает поток, по крайней мере, в Scala 2.11.
jaco0646 03

2
@ jaco0646 - не закрывает поток. Я только что протестировал. Вот демонстрационный код, который это доказывает: gist.github.com/RichardBradley/bcd1a5e61fcc83e4e59f8b9b0bc2301c
Rich

13

Более быстрый способ сделать это:

    private def inputStreamToString(is: InputStream) = {
        val inputStreamReader = new InputStreamReader(is)
        val bufferedReader = new BufferedReader(inputStreamReader)
        Iterator continually bufferedReader.readLine takeWhile (_ != null) mkString
    }

"Быстрее"? Но он дал мне ответ на вопрос, как это сделать, когда у меня просто есть, Readerа не файл InputStream.
BeepDog

3
Просто пропустите первую строку и перейдите inputStreamReaderк методу.
Камиль Лелонек

1
Это потенциально на порядок быстрее, чем scala.io.Source в Scala 2.11.7. Я написал действительно базовый тест, и в большинстве случаев он был примерно на 5% быстрее для больших файлов (тест был текстовым файлом размером ~ 35 МБ) и до 2800% быстрее для небольших файлов (тест был ~ 30 КБ) .
Колин Дин,

2
Прекрасный. Пытался найти элегантное решение, читая большие данные из Runtime.exec(). Это прибивает.
Павел Лечев

Как мне указать набор символов для использования?
Wheeler
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.