Mostrando las entradas con la etiqueta Scanner. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Scanner. Mostrar todas las entradas

domingo, 20 de julio de 2014

BufferedReader vs Scanner


En el lenguaje java hemos visto dos de las clases más usadas para lectura que son el BufferedReader y el Scanner. En resumen sabemos que el Scanner trabaja con tokens que son cadenas de caracteres que se separan mediante delimitadores que por defecto el Scanner tiene el espacio y que el BufferedReader trabaja manualmente carácter por carácter. Usted como programador seguramente las has usado alguna vez y se ha preguntado:

¿Cuál es más fácil de usar?


Para saber esto hemos elaborado el siguiente ejemplo:
Se requiere leer los siguientes valores en una sola línea, como se muestran abajo.

hola 12345 12.22

Se deberán guardar los valores en variables de tipo de dato String, int y float, a continuación se brindaran algunas soluciones para poder resolver el anterior ejemplo.

Solución sencilla con Scanner:

Código:
import java.util.Scanner;
/**
 *
 * @author Luis
 */
public class Solucion1Scanner {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String caracteres = sc.next(); //Lectura con Scanner
        int entero = sc.nextInt();
        float enteroFlotante = sc.nextFloat();
        System.out.println("Palabra " + caracteres);
        System.out.println("Numero " + entero);
        System.out.println("Flotante " + enteroFlotante);
    }
}
Podemos observar que los métodos que tiene Scanner nos pueden apoyar en cuestión de lecturas con cualquier tipo de dato primitivo (exepto: el char), pero con el BufferedReader tenemos que arreglárnoslas para poder tener cualquier tipo de dato, aquí se brindaran dos tipos de soluciones.

Solución sencilla con BufferedReader :

Código:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
 *
 * @author Luis
 */
public class Solucion1BufferedReader {
    public static void main(String[] args) throws IOException{
        InputStreamReader flujoEntrada = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(flujoEntrada);
        String[] arreglo = br.readLine().split(" ");
        //el metodo split Divide los strings conforme a los espacios
        String caracteres = arreglo[0];
        int entero = Integer.parseInt(arreglo[1]);
        float enteroFlotante = Float.parseFloat(arreglo[2]);
        System.out.println("Palabra " + caracteres);
        System.out.println("Numero " + entero);
        System.out.println("Flotante " + enteroFlotante);
    }
}

Solución compleja con BufferedReader:

Codigo:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Pattern;
/**
 *
 * @author Luis
 */
public class Solucion2BufferedReader {
    public static void main(String[] args) {
        InputStreamReader flujoEntrada = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(flujoEntrada);
        try {
            String linea = br.readLine(); 
            String[] arregloStrings = linea.split(" "); 
            int numero = 0;
            String palabra = "";
            float numeroFlotante = 0;
            for (int i = 0; i < arregloStrings.length; i++) { //Recorremos nuestro arreglo de strings
                String expresionRegular = "[a-z]+";
                boolean bandera = Pattern.matches(expresionRegular, arregloStrings[i]);
                //El metodo matches retornara true si el string corresponde al expresion
                if (bandera == true) { 
                    palabra = arregloStrings[i]; //
                }
                expresionRegular = "[0-9]+";
                bandera = Pattern.matches(expresionRegular, arregloStrings[i]);
                if (bandera == true) {
                    numero = Integer.parseInt(arregloStrings[i]);
                }
                expresionRegular = "^[0-9]*[.][0-9]+$";
                bandera = Pattern.matches(expresionRegular, arregloStrings[i]);
                if (bandera == true) {
                    numeroFlotante = Float.parseFloat(arregloStrings[i]);
                }
            }
            System.out.println("Palabra " + numero);
            System.out.println("Numero " + palabra);
            System.out.println("Flotante " + numeroFlotante);
        } catch (IOException e) {
            System.out.println("Error");
        }
    }
}
Resultado del ejemplo anterior


Estas soluciones que fueron aplicadas al BufferedReader también podrán ser aplicadas con el Scanner, usted podrá elaborarlas sin problemas.
Quedo claro que en cuestión de comodidad el Scanner es amplio vencedor sobre el BufferedReader, ya que este nos hace trabajar con caracteres.

¿Cuál hace menor tiempo?


Ahora veremos quién es el mejor en tiempo con las siguientes pruebas que hemos realizado:
Primero elaboraremos un archivo de texto que será nuestra lectura por Scanner y BufferedReader, con el mismo numero de líneas y contenido de texto.

Codigo
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
/**
 *
 * @author Luis
 */
public class CrearArchivoPrueba {
    public static void main(String[] args) {
        InputStreamReader flujoEntrada = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(flujoEntrada);
        System.out.println("**Crear archivo de texto**\n" + "Ruta del archivo:");
        try {
            String ruta = br.readLine(); 
            System.out.println("Nombre del archivo:");
            String nombre = br.readLine();
            System.out.println("Texto:");
            String texto = br.readLine();
            System.out.println("Numero de lineas:");
            int numero = Integer.parseInt(br.readLine());
            System.out.println("Se ha finalizado");
            crearArchivo(ruta, nombre, texto, numero);
        } catch (IOException e) {
            System.out.println("Error");
        }
    }
    
    public static void crearArchivo(String ruta, String nombreDelArchivo,
            String texto, int numeroLineas) throws IOException {
        
        ArrayList lineas = new ArrayList<>();
        for (int i = 0; i < numeroLineas; i++) { //Numero de lineas que seran agregadas al arraylist
            lineas.add(texto);
        }
        File archivo = new File(ruta + "\\" + nombreDelArchivo); 
        /*La clase File nos permite crear un archivo de tipo txt,
        en el constructor le agregamos el directorio donde queremos crear el archivo*/
        FileWriter archivoEscritura = new FileWriter(archivo, true);  
        //La clase FileWriter hace que podamos escribir caracteres dentro del archivo
        BufferedWriter bw = new BufferedWriter(archivoEscritura);
        //BufferedWriter permite crear un flujo de caracteres de salida
        for (int i = 0; i < lineas.size(); i++) {
            String linea = lineas.get(i);
            bw.write(linea); 
            bw.newLine(); //Salto de linea
        }
        bw.close(); // Cierra el flujo
    }
}
//

Ingresamos los datos como en el siguiente ejemplo:
Archivo de texto PruebaLineas.txt


Este archivo de texto será para realizar pruebas para nuestro métodos de lectura para toda la línea que tiene BufferedReader con readLine() y Scanner con nextLine().


Archivo de texto PruebaNext.txt


También este archivo se usara para pruebas con los métodos de Scanner next() y BufferedReader nextManual() “este método no lo tiene el BufferedReader por defecto”.
Bueno, para ver los resultados de las pruebas de BufferedReader y Scanner usaremos el siguiente código:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
/**
 *
 * @author Luis
 */
public class Pruebas {
    public static void main(String[] args) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("**Pruebas de BufferredRead y Scanner**\n" + "Metodos de BufferedReader:\n"
                + "a)readLine " + "b)nextManual\n" + "Metodos de Scanner:\n" + "c)nextLine " + "d)next");
        try {
            char letra = (char) br.read();
            long tInicial = System.currentTimeMillis(); // Para Medir el tiempo inicial
            switch (letra) {
                case 'a':
                    pruebaReadLine("PruebaLineas.txt"); //En el constructor ponemos el nombre de nuestro txt realizado                
                    break;
                case 'b':
                    pruebaNextManual("PruebaNext.txt");
                    break;
                case 'c':
                    nextLine("PruebaLineas.txt");
                    break;
                case 'd':
                    next("PruebaNext.txt");
                    break;
                default:
                    System.out.println("Caracter incorrecto");
                    break;
            }
            long tFinal = System.currentTimeMillis() - tInicial;
            double promedio = (double) tFinal / 1000000;
            /*Realizamos la operacion de forma directa para evitar que se tome 
             mas tiempo de esto con un contador*/
            System.out.printf("%.7f", promedio);
        } catch (IOException e) {
            System.out.println("Error " + e);
        }
    }

    public static void pruebaReadLine(String nombreArchivo) throws IOException {
        BufferedReader brFileReader = new BufferedReader(new FileReader(nombreArchivo));
        //La clase FileReader hace que podamos leer caracteres dentro del archivo        
        while (brFileReader.ready()) { //Mientras que el buffer no este vacio 
            String linea = brFileReader.readLine();
        }
        brFileReader.close();  // Se cierra el flujo
    }

    public static void pruebaNextManual(String nombreArchivo) throws IOException {
        BufferedReader brFileReader = new BufferedReader(new FileReader(nombreArchivo));
        while (brFileReader.ready()) {
            char caracter = '/'; //Se inicializa el char
            String palabra = ""; //Se incializa el string 
            while (true) {
                caracter = (char) brFileReader.read(); //Lee un caracter
                palabra += caracter;
                if (caracter == '\n' || caracter == ' ') { 
                  // Si es salto de linea o esta vacio se rompe el ciclo
                    break;
                }
            }
        }
        brFileReader.close();
    }

    public static void next(String nombreDelArchivo) throws IOException {
        Scanner scFile = new Scanner(new FileReader(nombreDelArchivo));
        while (scFile.hasNext()) { //Mientras tenga caracteres
            String palabra = scFile.next();
        }
    }

    public static void nextLine(String nombreDelArchivo) throws IOException {
        Scanner scFile = new Scanner(new FileReader(nombreDelArchivo));
        while (scFile.hasNextLine()) { //Mientras tenga una linea
            String linea = scFile.nextLine();
        }
    }
}

Metodos
Tiempo en milisegundos
readLine()
0.0005620
nextManual()
0.0006850
nextLine()
0.0025680
next()
0.0015290
                      Tabla de resultados

Como se pudo observar el ganador en esta prueba fue el uso de la clase BufferedReader respecto a los métodos utilizados, podemos decir que si queremos elaborar un código optimo con mejor tiempo recomendamos usar esta clase.

¿Cuál es mejor?


Elegir una ganadora es muy complicado porque cada una tiene sus propias ventajas unas sobre otras, el programador deberá saber que es lo más conveniente para utilizar en su código por ejemplo si se quiere trabajar con una lectura de línea de forma manual para realizar trabajos sencillos se podrá utilizar el BufferedReader o bien queremos usar una manera más automatizada se usara el Scanner, usted tendrá la decisión de escoger la que crea más conveniente para su caso.

miércoles, 25 de junio de 2014

Lectura por teclado : Uso básico del Objeto Scanner.

El tema central es la lectura por teclado, utilizando el objeto Scanner que se encuentra en la API de java.

El paquete donde se cuentra es :
       java.util.Scanner
A la hora de utilizarlo quedaría de esta manera :
      import  java.util.Scanner;

Teniendo en cuenta esto, vamos a empezar:

Primero: 
  • ¿Qué es lo que queremos leer? 
  • ¿Qué es lo que podemos leer con Scanner?
Nos centraremos en el uso básico del Scanner.
Contestando la primera pregunta:

¿Qué es lo que queremos leer con Scanner?

La mayoría de los usuarios quieren ingresar en un programa, dependiendo su finalidad:
  • Números.
  • Palabras.
El primer punto, los números hay diferentes tipos: enteros , decimales , números pequeños, extremadamente grandes, etc...

Sobre las palabras, son generales y se pueden utilizar de diversas maneras, según el objetivo del programa.

Por el lado de los números, lo que le debe de importar al programador es que finalidad va a tener su programa. El tipo de número que tiene que utilizar.

¿Qué es lo que podemos leer con Scanner?

En Java existen los tipos de datos primitivos.

  • Hablando de valores números se presentan: 

Nombre   Tipo       Ocupa       Rango Aproximado

byte         Entero     1 byte      -128 a 127

short        Entero     2 bytes    -32768 a 32767

int            Entero     4 bytes    -2147483648 a 2147483647

long         Entero     8 bytes    -9223372036854775808 a 9223372036854775807

Nombre            Tipo              Ocupa       Rango Aproximado

float        Decimal simple     4 bytes       ± 3.4x10-38 a ± 3.4x1038

double    Decimal doble      8 bytes       ± 1.8x10-308 a ± 1.8x10308

*Es importante que el programador tenga en cuenta el rango aproximado  del tipo de dato a utilizar. Dado que  si no se manejan correctamente pueden desbordarse y dar valores incorrectos

**Desbordarse : superar su rango aproximado, por ende dar un valor incorrecto.

En Java, para cada tipo de dato existe una clase asociada en la API. Todas ellas se encuentran en java.lang las cuales vienen por defecto.

Tipo de Dato       Clase en Java

byte                          Byte
short                         Short
int                             Integer
long                          Long
float                          Float
double                      Double

Todos los tipos de datos numéricos, cuando los creamos vienen con 0 [cero] como valor por defecto.

Los valores del tipo numérico entero es un número real, pueden ser positivos o negativos.

Los valores del tipo numérico decimal son números  que llevan un punto decimal y que pueden ser positivos o negativos.


  • Hablando de cadenas de caracteres Java presenta:

Nombre       Tipo                Ocupa       Rango Aproximado       Clase en Java

char           Carácter simple      2 byte       Caracteres Unicode        Character

  • Hablando de datos lógicos se encuentra: 

Nombre           Tipo               Ocupa       Rango          Clase en Java

boolean          Dato lógico     1 byte       true - false         Boolean


¿ Y cómo leemos esos valores con el Scanner?

Primero se crea una instancia del objeto Scanner. Lo que recibe en el constructor es el flujo del teclado. *Se agrega System.in [flujo de entrada].

     Scanner s = new Scanner (System.in); 


Concluido lo anterior, Scanner tiene métodos para lectura de los datos primitivos presentados, con la facilidad de utilizar la variable del Scanner y llamar al método para el correspondiente tipo de dato.

       Numérico : 

       Tipo de Dato     Forma de Lectura
       
        byte                     .nextByte();
        short                    .nextShort();
        int                        .nextInt();
        long                     .nextLong();
        float                     .nextFloat();
        double                .nextDouble();

        Cadena de Caracteres:

       Tipo de Dato     Forma de Lectura

        char              No Aplica con Scanner.
        String          .next()
        String          .nextLine()

        Dato Lógico

       Tipo de Dato     Forma de Lectura

        boolean             .nextBoolean();

*Para leer Cadena de caracteres se utiliza el objeto String y los métodos antes mencionados. La diferencia radica en :

.next() - Lee una linea que el usuario introduzca hasta donde se encuentre un espacio.

   Ejemplo :
       String ejemplo = s.next()       // El usuario ingresa por teclado :   hola mundo

  Cuando el usuario imprima la variable ejemplo se mostrará :
    hola    // El método next() solo lee hasta encontrar un espacio en blanco.

.nextLine() - Lee una linea completa que introduzca el usuario, no importando si esta separada por espacios.

**Para leer un tipo de dato lógico, solo se acepta que el usuario ingrese true-false, tanto en mayúsculas como minúsculas, de otra manera se arrojará una excepción

Se crean 6 variables numéricas con sus respectivo tipo de dato:

byte                 
     byte a = s.nextByte();

short
     short corto = s.nextShort();

int
     int entero = s.nextInt();

long
     long largo = s.nextLong();

float
     float flotante= s.nextFloat();

double
     double doble = s.nextDouble();

Para finalizar, solo se mandan a imprimir. El código quedaría de la siguiente manera:


    public static void leerDatosNumericos() {
        
        Scanner s = new Scanner(System.in);
        
        byte a = s.nextByte();
        short corto = s.nextShort();
        int entero = s.nextInt();
        long largo = s.nextLong();
        float flotante = s.nextFloat();
        double doble = s.nextDouble();

        System.out.println("El byte es : " + a);
        System.out.println("El short es : " + corto);
        System.out.println("El int es : " + entero);
        System.out.println("El long es : " + largo);
        System.out.println("El float es : " + flotante);
        System.out.println("El double es : " + doble);
    }

Para leer cadenas de texto, se usan los métodos antes mencionados :

String cadena = s.next();
String cadena2 = s.nextLine();


*Problema : Al leer con Scanner, en algunos métodos de lectura dejan basura, ya sea el retorno de carro [\r]  o nueva linea [\n]. En el buffer del flujo de entrada se queda la basura mencionada.

**Para  resolver lo anterior , basta con poner el método leer Cadena Completa [s.nextLine()] para que quite la basura. El problema mencionado surge a la hora de leer con un .next() o después de leer un número y siempre, en ambos casos siga el leer un String. En C o C++ existe algo parecido, el fflush () , por si están acostumbrados a esos lenguajes una comparación.


Finalizando el método de lectura de cadenas de la siguiente manera:

    public static void leerCadenas() {
        Scanner s = new Scanner(System.in);

        String cadena1 = s.next();
        
        s.nextLine(); // Para limpiar el buffer
        
        String cadena2 = s.nextLine();

        System.out.println("Cadena 1 : " + cadena1);
        System.out.println("Cadena 2 : " + cadena2);
        
    }

Para leer tipos de dato lógicos es de la siguiente manera:

    public static void leerDatoLogico() {
        Scanner s = new Scanner(System.in);
        
        boolean boleano = s.nextBoolean();
        
        System.out.println("El boolean es : "+boleano);
    }

Con esto termina el tema. Lectura básica del teclado con el Objeto Scanner.