C贸mo obtener la entrada del usuario en Java

    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.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *