Come leggere riga per riga dallo standard input?


91

Qual è la ricetta di Scala per leggere riga per riga dallo standard input? Qualcosa di simile al codice java equivalente:

import java.util.Scanner; 

public class ScannerTest {
    public static void main(String args[]) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            System.out.println(sc.nextLine());
        }
    }
}

Risposte:


130

L'approccio più diretto userà solo quello readLine()che fa parte di Predef. tuttavia è piuttosto brutto in quanto è necessario verificare l'eventuale valore nullo:

object ScannerTest {
  def main(args: Array[String]) {
    var ok = true
    while (ok) {
      val ln = readLine()
      ok = ln != null
      if (ok) println(ln)
    }
  }
}

questo è così dettagliato, preferiresti usare java.util.Scannerinvece.

Penso che un approccio più carino utilizzerà scala.io.Source:

object ScannerTest {
  def main(args: Array[String]) {
    for (ln <- io.Source.stdin.getLines) println(ln)
  }
}

3
il metodo readLine di Predef è stato deprecato dalla 2.11.0, ora si consiglia di utilizzare il metodo inscala.io.StdIn
— nicolastrres

1
@itemState il mio programma non finisce, se uso, "io.Source.stdin.getLines" va in modalità di attesa ... come gestirlo ...
— Raja

53

Per la console puoi usare Console.readLine. Puoi scrivere (se vuoi fermarti su una riga vuota):

Iterator.continually(Console.readLine).takeWhile(_.nonEmpty).foreach(line => println("read " + line))

Se cat un file per generare l'input potrebbe essere necessario fermarsi su null o vuoto utilizzando:

@inline def defined(line: String) = {
  line != null && line.nonEmpty
}
Iterator.continually(Console.readLine).takeWhile(defined(_)).foreach(line => println("read " + line))

Conosco Console.readLine (), sto cercando una determinata ricetta. Il modo "scala" per leggere riga per riga dallo Standard input.
— Andrei Ciobanu

11
Penso che intenditakeWhile(_ != null)
— Seth Tisue

1
Dipende da come vuoi fermarti. Cercare una riga vuota è spesso la soluzione più semplice.
— Landei

4
Nota che da Scala 2.11.0 Console.readLineè deprecato, usa StdIn.readlineinvece.
— Bartłomiej Szałach

Oppure .takeWhile(Option(_).nonEmpty)potresti sentirti meglio nel caso in cui desideri evitare nullcompletamente la parola chiave.
— conny

27
val input = Source.fromInputStream(System.in);
val lines = input.getLines.collect

6
io.Source.stdinè definito (in scala.io.Sourceclasse), def stdin = fromInputStream(System.in)quindi probabilmente è meglio attenersi al io.Source.stdin.
— Nader Ghanbari

Questo non sembra funzionare con Scala 2.12.4, o non ho trovato le cose giuste da importare.
— akauppi

Funziona in Scala 2.12, solo che il collectmetodo viene cambiato dopo questa risposta, quindi devi solo chiamare input.getLinesche ti dà un Iterator. Puoi costringerlo a materializzarsi usando .toStreamo .toListsu di esso, dipende dal caso d'uso.
— Nader Ghanbari

11

Una versione ricorsiva (il compilatore rileva una ricorsione in coda per un migliore utilizzo dell'heap),

def read: Unit = {
  val s = scala.io.StdIn.readLine()
  println(s)
  if (s.isEmpty) () else read 
}

Notare l'uso di io.StdInda Scala 2.11. Nota anche che con questo approccio possiamo accumulare l'input dell'utente in una raccolta che alla fine viene restituita, oltre a essere stampata. Vale a dire,

import annotation.tailrec

def read: Seq[String]= {

  @tailrec
  def reread(xs: Seq[String]): Seq[String] = {
    val s = StdIn.readLine()
    println(s)
    if (s.isEmpty()) xs else reread(s +: xs) 
  }

  reread(Seq[String]())
}

10

Non puoi usare

var userinput = readInt // for integers
var userinput = readLine 
...

Come disponibile qui: API Scaladoc


questo non è equivalente al codice presentato con loop
— techkuz

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.