Cómo obtener la entrada del usuario en Java

C

Introducción

Leer la entrada del usuario es el primer paso para escribir un software Java útil. La entrada del usuario puede tener muchas formas: interacciones con el mouse y el teclado, una solicitud de red, argumentos en la línea de comandos, archivos que se actualizan con datos relevantes para la ejecución de un programa, etc.

Nos centraremos en la entrada del teclado a través de algo llamado el flujo de entrada estándar. Puede reconocerlo como Java System.in.

Vamos a usar Scanner class para facilitar nuestra interacción con el flujo subyacente. Ya que Scanner tiene algunas desventajas, también usaremos el BufferedReader y InputStreamReader clases para procesar el System.in corriente.

Al final, decoraremos el InputStream clase e implementar nuestra propia costumbre UncloseableInputStream para manejar problemas con el Scanner clase.

Clase de escáner Java

los java.util.Scanner class es un escáner simple que puede analizar y manejar entradas primitivas, cadenas y flujos. Ya que System.in es solo un InputStream, podemos construir un Scanner como tal:

Scanner sc = new Scanner(System.in);

Esta Scanner La instancia ahora puede escanear y analizar valores booleanos, enteros, flotantes, bytes y cadenas.

Veamos cómo podemos extraer información de un Scanner en variables con las que podemos trabajar:

Scanner sc = new Scanner(System.in);
        
// Read an integer into a variable
int myInteger = sc.nextInt();
        
// Read a byte into a variable
byte myByte = sc.nextByte();
        
// Read a line until newline or EOF into a string
String myLine = sc.nextLine();
        
// Closing the scanner
sc.close();

Nuevamente, el constructor no tiene que tomar System.in. Puede tomar cualquier File, InputStream, Readable, ReadableByteChannel, Path (de un archivo para leer), o incluso String. Adicionalmente, como segundo argumento, puede especificar una codificación de caracteres para interpretar dichos caracteres por:

Scanner sc = new Scanner(new FileInputStream("myFile.txt"), "UTF-8");

Tenga en cuenta que Scanner debe cerrarse cuando termine de trabajar con él. La forma más sencilla de hacerlo es a través de la declaración try-with-resources.

Métodos de lectura para escáner

Los métodos disponibles para leer el siguiente token utilizando el método del escáner son:

MétodoTipo de retornoDescripción
siguiente()StringBusca y devuelve el siguiente token completo del escáner.
nextByte ()byteEscanea el siguiente token de la entrada como un byte.
nextDouble ()dobleEscanea el siguiente token de la entrada como un doble.
nextFloat ()flotadorEscanea el siguiente token de la entrada como flotante.
nextInt ()En tEscanea el siguiente token de la entrada como int.
nextLong ()largoEscanea el siguiente token de la entrada como un long.
nextShort ()cortoEscanea el siguiente token de la entrada como corto.
nextBoolean ()booleanoEscanea el siguiente token de la entrada en un valor booleano y devuelve ese valor.
línea siguiente()StringAvanza este escáner más allá de la línea actual y devuelve la entrada que se omitió.

Un método digno de mención es el hasNext() método: un método genérico que devolverá true si hay algún tipo de token para leer. Hay métodos específicos de tipo como hasNextInt(), hasNextFloat(), hasNextLine() etc., que puede utilizar de la misma forma.

Problemas al usar System.in con el escáner

Un gran problema con System.in es que es un InputStream. Al trabajar con él, el Scanner siempre esperará más información hasta que InputStream está cerrado. Una vez que se cierra la transmisión, ya no podemos acceder a la entrada desde el Scanner.

Además de cerrarse, el Scanner la clase también cerrará el InputStream si implementa Closeable.

Ya que InputStream hace, eso significa que el Scanner cerrará el System.in transmitir a la totalidad de su programa.

Dicho esto, si cierras un Scanner y así el System.in también, no puedes usar System.in otra vez:

Scanner sc = new Scanner(System.in);
System.out.println(sc.nextInt());
sc.close();
System.out.println("Closing the scanner...");

sc = new Scanner(System.in);
System.out.println(sc.nextInt());
sc.close();
System.out.println("Closing the scanner...");

Esto resulta en:

1
1
Closing the scanner...
Exception in thread "main" java.util.NoSuchElementException
	at java.util.Scanner.throwFor(Scanner.java:862)
	at java.util.Scanner.next(Scanner.java:1485)
	at java.util.Scanner.nextInt(Scanner.java:2117)
	at java.util.Scanner.nextInt(Scanner.java:2076)
	at com.company.Main.main(Main.java:18)

Esto hace trabajar con Scanner y System.in mucho más complicado. Arreglaremos esto en la sección final.

BufferedReader y InputStreamReader

En vez de una Scanner, también puede utilizar un BufferedReader a lo largo de un InputStreamReader para obtener la entrada del usuario:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

String line;

while((line = br.readLine()) != null){
    System.out.println(String.format("The input is: %s", line));
}

Aquí, simplemente repetimos la Cadena de entrada con un prefijo:

Hello!
The input is: Hello!
I'd like to order some extra large fries.
The input is: I'd like to order some extra large fries.
^D

Process finished with exit code 0

los BufferedReader es adecuado para leer cadenas, pero no tiene métodos integrados para manejar números. Para leer un número entero, tendría que analizarlo desde una cadena:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

int a = Integer.parseInt(br.readLine());
System.out.println(a);

Esto funciona bien ahora:

5
5

InputStream personalizado que no se puede cerrar

Afortunadamente, hay una solución para Scanner cerrando el System.in Stream gracias al Patrón de diseño del decorador. Podemos implementar nuestro propio InputStream y solo haz el close() método no hacer nada para que cuando Scanner lo llama, no afectará la entrada estándar subyacente:

public class UnclosableInputStreamDecorator extends InputStream {

    private final InputStream inputStream;

    public UnclosableInputStreamDecorator(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    @Override
    public int read() throws IOException {
        return inputStream.read();
    }

    @Override
    public int read(byte[] b) throws IOException {
        return inputStream.read(b);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return inputStream.read(b, off, len);
    }

    @Override
    public long skip(long n) throws IOException {
        return inputStream.skip(n);
    }

    @Override
    public int available() throws IOException {
        return inputStream.available();
    }

    @Override
    public synchronized void mark(int readlimit) {
        inputStream.mark(readlimit);
    }

    @Override
    public synchronized void reset() throws IOException {
        inputStream.reset();
    }

    @Override
    public boolean markSupported() {
        return inputStream.markSupported();
    }

    @Override
    public void close() throws IOException {
        // Do nothing
    }
}

Cuando modificamos nuestro código problemático para usar el código personalizado InputStream, se ejecutará sin problemas:

public class ScannerDemo {
    public static void main(String[] args) {
    
        Scanner sc = new Scanner(new UnclosableInputStreamDecorator(System.in));
        System.out.println(sc.nextInt());
        sc.close();
        System.out.println("Closing the scanner...");

        sc = new Scanner(new UnclosableInputStreamDecorator(System.in));
        System.out.println(sc.nextInt());
        sc.close();
        System.out.println("Closing the scanner...");
    }
}

Ejecutar esto resultará en:

1
1
Closing the scanner...
1
1
Closing the scanner...

Conclusión

En este artículo, hemos cubierto cómo usar el Scanner class para leer la entrada del usuario. Luego usamos el BufferedReader clase a lo largo del InputStreamReader como un enfoque alternativo.

Finalmente, hemos implementado nuestro propio InputStream para evitar el problema de Scanner cerrando el System.in Stream para todo el programa.

Con suerte, ha aprendido a manejar la entrada de la consola básica en Java y algunos errores comunes que puede encontrar en el camino.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad