Ідіоматичний спосіб перетворення 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
  }

Чи є ідіоматичний спосіб зробити це в масштабі?

Відповіді:


197

Для Scala> = 2.11

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

Для Scala <2,11:

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

робить майже те саме. Не впевнений, чому ви хочете отримати лінії, а потім склеїти їх усі разом, хоча. Якщо ви можете припустити, що блокування потоку не блокується, ви можете просто використовувати .available, прочитати все в байтовому масиві та безпосередньо створити рядок із цього.


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

Відповідь Раама також є приголомшливою (і трохи більш стислою), але позначення Рекса як НАЙ відповідь, оскільки це конкретніше як приклад. Склеювання ліній разом було специфічним у кількох випадках, але ви нагадали мені, що я використовував цей код у місцях, де це не зовсім доречно.
бабалант

рішення не дуже безпечне, оскільки використовує getLines (); що робити, якщо в потоці введення немає символів "нового рядка"? то вся справа блокується
Пол Сабу

Досить погане рішення. Що робити, якщо вхідний потік містить закінчення рядків DOS (\ r \ n). Вони будуть видалені цим методом. Крім того, хоча mkString використовує буфер, але, швидше за все, було б швидше читати блоки символів.
Діббеке

1
@RexKerr Чи можете ви, будь ласка, вказати на "помилку щодо продуктивності", про яку ви згадали у своїй відповіді. Я перевірив обидві версії з деякими основними тестами, і жодного питання не потрапив.
Sahil Sareen

74

Source.fromInputStream(is).mkString("") теж зробить діло .....


Гарна думка; джерело створює щось, що розширюється Iterator[Char].
Рекс Керр

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

Це стисло, але він не закриває потік, тоді як оригінальний код Java був.
Багатий

@Rich, fromInputStream()схоже, закриває потік, принаймні, у Scala 2.11.
jaco0646

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. Джерело в Scala 2.11.7. Я написав дійсно базовий орієнтир, і більшу частину часу це було приблизно на 5% швидше для великих файлів (тест склав ~ 35 Мб текстовий файл), аж до 2800% швидше для невеликих файлів (тест склав ~ 30 КБ) .
Колін Дін

2
Гарний. Боровся за елегантне рішення, читаючи великі матеріали з Runtime.exec(). Це цвяхи це.
Павло Лечев

Як я можу вказати набір символів для використання?
Уїлер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.