sudom82 sudom82 - 1 month ago 15
Scala Question

Converting disrete chunks of Stdin to usable form

Simply, if the user pastes a chunk of text (multiple lines) into the console all at once, I want to be able to grab that chunk and use it.

Currently my code is

val stringLines: List[String] = io.Source.stdin.getLines().toList
doStuff(stringLines)


However doStuff is never called. I realize the stdin iterator doesn't have an 'end', but how do I get the input as it is currently? I've checked many SO answers, but none of them work for multiple lines that need to be collated. I need to have all the lines of the user input at once, and it will always come as a single paste of data.

Answer

This is a rough outline, but it seems to work. I had to delve into Java Executors, which I hadn't encountered before, and I might not be using correctly.

You might want to play around with the timeout value, but 10 milliseconds passes my tests.

import java.util.concurrent.{Callable, Executors, TimeUnit}
import scala.util.{Failure, Success, Try}

def getChunk: List[String] = {  // blocks until StdIn has data
  val executor = Executors.newSingleThreadExecutor()
  val callable = new Callable[String]() {
    def call(): String = io.StdIn.readLine()
  }

  def nextLine(acc: List[String]): List[String] =
    Try {executor.submit(callable).get(10L, TimeUnit.MILLISECONDS)} match {
      case Success(str) => nextLine(str :: acc)
      case Failure(_)   => executor.shutdownNow() // should test for Failure type
                           acc.reverse
    }

  nextLine(List(io.StdIn.readLine()))  // this is the blocking part
}

Usage is quite simple.

val input: List[String] = getChunk