miércoles, 29 de abril de 2009

La Clase Scanner de java 5.

La clase Scanner que se introdujo a partir de java 5 tiene varias utilidades. Puede separar o romper su dato de entrada en subcadenas o “tokens” y además convertir estos datos en tipos primitivos como enteros, flotantes, etc. Por ejemplo, puedo pasarle un dato String como argumento al constructor, definir su patrón delimitador (por defecto: espacio en blanco) y obtener subcadenas cuyos miembros debo recoger uno a uno mediante el método next() o algunos de sus similares (nextInt(), nextByte()… para cada dato primitivo existe un next()), veamos un pequeño programa:

public static void main(String[] args) {
    String cadena = "Blog_Acerca_de_Java";
    Scanner s = new Scanner(cadena);
    s.useDelimiter("_");
    while(s.hasNext()){
        System.out.println(s.next()); //s.next() devuelve cadena token
    }
}

En este programa se separarán en subcadenas separando por “_” y se muestra cada término en una línea en la consola de java. Este tipo de uso es muy similar a usar la clase StringTokenizer.
Por otro lado, la clase Scanner también tiene un constructor que recibe un InputStream, por lo que podemos pasarle al mismo la sentencia System.in para leer datos desde el teclado:

public static void main(String[] args) {
    System.out.print("Introduzca datos: ");
    Scanner s = new Scanner(System.in);
    System.out.println(s.next());
    System.out.println(s.nextInt());
    System.out.println(s.nextLine());
}

Este último código lee datos a partir del teclado, separa en subcadenas con espacio en blanco como delimitador, lee la primera cadena y la imprime en consola, espera un espacio, después busca el siguiente dato e intenta convertirlo a entero (int) si no puede hacerlo lanza un InputMismatchException y se detiene, de no lanzar la excepción lee el siguiente dato hasta el final de la línea y lo imprime.
Otro constructor que también nos beneficia es el que recibe un archivo como parámetro. Supongamos que tenemos un archivo con esta información:

Kelvin
30
78,7
Horario: 8am a 4pm

Los datos de este archivo pueden ser tratados mediante el siguiente programa:

public static void main(String[] args) {
    try {
        Scanner s = new Scanner(new File("archivo.txt"));
        System.out.println(s.nextLine());
        System.out.println(s.nextInt());
        s.nextLine();
        System.out.println(s.nextDouble());
        s.nextLine();
        System.out.println(s.nextLine());
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (InputMismatchException e){
        e.printStackTrace();
    }
}

Recuerde colocar el archivo en la misma ruta el paquete del programa. Que fácil en comparación a los códigos de la versión anterior, ahora con la clase Scanner el manejo de archivos de acceso secuencial es mucho más fácil. Bueno, por ahora no tengo más que decir, espero esta información sea de utilidad, hasta una próxima edición.

Más información: Clase Scanner API Java

27 comentarios:

Ministerio de Adoracion, Iglesia Bautista Del Amor dijo...

Muy interesante tu artículo, me sirvió mucho, salu2!

victor dijo...

buen dato! cual seria su equivalente para la escritura de datos en archivos txt con esto se pueden enviar mensajes como en sockets, x ejemplo, con varias lineas?

Kelvin dijo...

Bueno, la clase Scanner solo funciona para leer datos y no para escribir, es bueno aclararlo.

victor dijo...

Kelvin.

Gracias, eso lo tengo en claro, yo necesitaba escribir varias lineas en un archivo por eso era mi consulta, pero navegando encontre que se puede con PrintWriter. Ahora me encuentro con otra dificultad que esta en poner algun tipo de timeout a la espera de flujos de entrada de un socket, para que la clase que estoy trabajando no se quede a la espera cuando no hayan datos de entrada. Alguien con alguna idea.

Kelvin dijo...

Victor: Yo creo que podría servirte, un ciclo while que se mantenga leyendo la entrada, algo así:

int contador = 10;
while(entrada == null){/*O como tu puedas leer la entrada*/
try{
Thread.sleep(10);/*Dormir 10 miliseg*/
}catch(InterruptedException e){
e.printStackTrace();
}
if(contador >= 10000)/*En 10 segundos parar*/
break;
contador+=10;
}

Espero que esto te sira y puedas adaptarlo a tu código, si ves que no te sirve me avisas y eguimos tratando

victor dijo...

Kelvin.
Gracias, aun no lo pruebo porque mi aplicacion no me esta botando errores al conectar, pero de todos modos lo probare. Tengo otra consulta. La clase q estoy creando se conecta con mysql, mi duda es: como hago para asignar a una variable un campo de algun registro dado.
Estoy trabajando con
Connection conexion = DriverManager.getConnection(
"jdbc:mysql://localhost/facilidades", "root", "");
Statement s = conexion.createStatement();

ResultSet rs = s.executeQuery(.... aca van el query)

pero necesito asignar a una variable del tipo String un campo del mismo tipo :

tmp = rs.getString("campo");

me da error:



Pero al compilar me da error.

at pares.conectMysql.main(conectMysql.java:25)

Tambien tengo q hacer una logica que me permita saber si hay dato en este campo o no.

Espero me ayuden... Help...

Kelvin dijo...

Tu ejemplo te falla por una razon, el resulset puede tener varias filas, para hacer el recorrido debes hacer whilee(rs.next()){} de la siguiente manera:

try {
Class.forName("com.mysql.jdbc.Driver");
Connection conexion = DriverManager.getConnection("jdbc:mysql://localhost/facilidades", "root", "");
Statement s = conexion.createStatement();
ResultSet rs = s.executeQuery("SELECT a, b FROM tabla"); while(rs.next()){
String cadena = rs.getString("b");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}

Además debes asegurarte de hacer la importación
import java.sql.*;
y no una de jdbc ni mysql//Error común

victor dijo...

kelvin.

Gracias podrias agregarme a viajero_sin_rumbo en hot mail?

Steven dijo...

Que tal, estoy haciendo validaciones a un archivo, y una de ellas es verificar que no contenga registros duplicados. la estructura del archivo es el siguiente
|1|Nombre|Apellido|Direccion|
|2|Nombre|Apellido|Direccion|
|3|Nombre|Apellido|Direccion|
|4|Nombre|Apellido|Direccion|

en donde la primer columna funje como llave primaria de una tabla en donde se cargara dicho archivo, la cuestion es que necesito validar que en la primer columna no existan valores duplicados, es posible con la clase scanner agradezco la ayuda.

Kelvin dijo...

La clase Scanner trabaja sólo para recibir datos, lo que tu desees hacer con los datos ya no es parte de la clase Scanner, es decir, si deseas validar debes usar funciones o métodos que puedan hacerlo, la clase sólo puede ayudarte a decidir cual es entero, float, double o cadena.
Si tu base de datos tiene como primera columna una clave primaria al tratar de ingresar valores repetidos se arroja un error y de ser así puedes mostrar un mensaje recalcando que hay datos inválidos.
Mas a detalle en código:
boolean resultado = statement.execute(sql);
resultado es false, y por lo tanto hay datos invalidos.
Si no quieres que se agregue ningún valor si hay datos repetidos debes hacer uso de transacciones. Cualquier duda o si quieres más detalles no dudes en escribir.

Gary dijo...

Hola, muchisimas gracias por el articulo, realmente me has salvado xD crei que jamas encotraria una forma ajajaj de leer xD (facil), ahora con una duda, supon que tengo un archivo separdo por comas (,), y quiero que leea y guarde en una matriz, como lo haria, supon el archivo de este tipo

a,,b,c,,a,a,a,b
a,,b,c,,a,a,a,b
a,,b,c,,a,a,a,b
a,,b,c,,a,a,a,b
a,,b,c,,a,a,a,b

donde a,b,c son palabras cualkiera, y el espacio en blanco es eso, nada :D me podrias echar una mano? gracias de nuevo por el articulo

Kelvin dijo...

Para escribir ese archivo (supongamos que se llama comentario.txt y esta en la ruta -- C:\RutaArchivo.

El código sería:

import java.io.*;
import java.util.*;

public class RespuestaComentarioGary {
  public static void main(String[] args) {
    String[][] matriz = new String[5][9];//nro de filas y columnas
    try {
      Scanner s = new Scanner(new File("C:\\RuraArchivo\\archivo.txt"));
      s.useDelimiter("\n");//delimitador: salto de linea
      for(int i = 0; s.hasNext() && i < matriz.length; i++){
       String fila = s.nextLine();
       Scanner nuevoScanner = new Scanner(fila);
       nuevoScanner.useDelimiter(",");
       for(int j = 0; nuevoScanner.hasNext() && j < matriz[i].length; j++){
        try {
          matriz[i][j] = nuevoScanner.next();
        } catch (NoSuchElementException e) {//Si No hay nada
          matriz[i][j] = "";
        }
       }
      }
      for(int k = 0; k < matriz.length; k++){
       for(int l = 0; l < matriz[k].length; l++){
        System.out.println("Matriz["+k+"]["+l+"]="+matriz[k][l]);
       }
      }
    } catch (FileNotFoundException e) {
      System.out.println("Archivo No encontrado");

      e.printStackTrace();
    }
  }
}

No dudes en escribie si tienes otra duda

Gary dijo...

Muchísimas gracias, veras, tu programa funciona a la perfección, con la excepción de que no reconoce el inicio de la fila si esta esta vacia, yo con mis pocos xD conocimientos de java, también hice uno, nada tan elaborado, pero me sucede lo mismo :(.

supon el imput este:


,gato,,,gato,gato
,,gato,gato,gato,gato
gato,,,gato,gato

el programa, no reconose el primer caracter que seria, "" en cambio comienza a leer a partir de gato.

esto, se prodra solucionar? bueno discupa las molestias y muchas gracias por responder :D
saludos

Kelvin dijo...

Fíjate bien.

Pasé la variable matriz como variable de clase, es decir, variable estática de la clase Respuesta comentario:

static String[][] matriz = new String[3][6];//nro de filas y columnas

Luego inicializo todo los elementos como cadena vacía, esto lo hago en caso de que haya un valor nulo para que quede como cadena vacía.
static{
  for(int i = 0; i < matriz.length; i++){
    for(int j = 0; j < matriz[i].length; j++){
      matriz[i][j] = "";
    }
  }
}

Ahora se agrega un código que verifica si la línea comienza por coma:

for(int i = 0; s.hasNext() && i < matriz.length; i++){
  String fila = s.nextLine();
  aux = 0;
  while(fila.startsWith(",")){
    matriz[i][aux]="";/*asigno vacio*/
    fila = fila.substring(1);/*elimino coma*/
    aux++;
  }
  Scanner nuevoScanner = new Scanner(fila);
  nuevoScanner.useDelimiter(",");
  for(int j = aux; nuevoScanner.hasNext() && j< matriz[i].length; j++){
    try {
      matriz[i][j] = nuevoScanner.next();
    } catch (NoSuchElementException e) {/*No hay nada*/
      matriz[i][j] = "";
    }
  }
}

Lo demás sigue igual al código de mi comentario pasado. No olvides que si tienes alguna duda puedes preguntar, ya que esta información te puede servir a ti o a otras personas. Chao

Gary dijo...

Funciona perfectamente, gracias :)

Integer dijo...

que buen blog, es excelente; me ayudo mucho en mi problema. Gracias,
!Good Work!

Anónimo dijo...

Hola a todos!

Tengo un problema que no se como solucionar.... tengo un fichero de esta manera:

AU Gutierrez, M
Marin, C
Bonachea, J
AF Gutierrez, Mateo
Lucha, Pedro
Gutierrez, Francisco
TI Are talus flatiron sequences in Spain climate-controlled landforms?
SO ZEITSCHRIFT FUR GEOMORPHOLOGIE
LA English
DT Article

Cada empiece con 2 letras mayusculas es un campo, y quiero leerlo y que interprete que es un campo diferente, es decir AU un campo con sus autores: Gutierrez, marin y Bonachea, estos autores que los meta todos juntos en un string, y AU en otro.
Estoy intentando leyendolo linea por linea, luego separarlo por tokens, y concatenar, pero no me sale nada!!!

haber si me podeis ayudar,

un saludo,
Toño.

Kelvin dijo...

Saludos, Toño:

Para responder tu pregunta he creado un nuevo post, visita este enlace en Acerca de Java:

Respuesta Clase Scanner

Hasta Luego

marisela dijo...

Hola soy mary quisiera hacerte una pregunta es tengo k hacer un programa k lea una cadena y k valla extrayando una palabra y valla poniendo cada palabra en un renglon asi:
"la
niña
esta
sentada"
y lo tengo k hacer con el scanner pero tengo muchas dudas me podrias ayudar????

Kelvin dijo...

Debes pasarle la cadena como parámetro al constructor de Scanner, luego hacer recorrido y agregar al final un salto de linea \n, así:

String cadena ="La niña esta sentada";
String resultado = "";

Scanner s = new Scanner(cadena);

while(s.hasNext()){

resultado += s.next() + "\n";

}

System.out.println(resultado);

Espero te sirva de ayuda.

marisela dijo...

Grax si me sirvio mucho mmm tengo mas dudas bueno son consultas jeje es k tengo k hacer un proyecto, hacer un programa pero en la plataforma de eclipse, cres k me podrias asi como asesorar o algo asi? xfa me salvarias la vida

marisela dijo...

hola buen dia, tengo una duda en mi proyecto, es que no se como hacerle para buscar en una palabra una vocal especifica. Se pide que se introduzca el texto , ya sustraje las palabras y tengo que separar en silabas una palabra pero solo las palabras que contengan la "o" y separar en silabas solo si se encuentra la "o" , por ejemplo:
ca-rro-seria. Pienso primero hacer la busqueda de las palabras que contengan esa vocal y las que no ignorarlas.
Son varios casos y reglas ortograficas en donde se especifica como se separan las vocales eso ya lo tengo bien especificado, pero estoy atorada en eso porque para llevara cabo lo anterior tambien tengo que separar todas las letras de alguna palabra y comparar si es consonante o no y todo eso. Puedes ayudarme?

er nani dijo...

Hola, Kelvin, me llamo Manel, soy de Barcelona, España. Por lo que veo dominas bastante el JAVA. Yo estoy empezando....
La cuestión es que me gustaria saber porque cuando uso la clase Scanner repetidas veces en modo consola, hay ocasiones en las que se salta las entradas.
Parece como si el buffer del objeto instanciado de la clase Scanner no estuviera vacio del todo.
Si quieres puedo mandarte mi proyecto de Netbeans y le echas una ojeada.
Un abrazo a toda sud-america y en especial para Venezuela!!!

Kelvin dijo...

Saludos Er nani:

Una vez que hayas introducido una data a través del método read correspondiente en un Scanner, este queda almacenada en el contenido de él. Y lo que es peor aun es que no hay manera de devolverse para pedir de nuevo su información. Siempre lee hacia adelante.

Si necesitas que quede borrado su contenido debes volver a crear una instancia antes de volver a leer:

s = new Scanner(System.in);
s.read();

donde s es el nombre de tu instancia Scanner y utiliza el read correspondiente para leer un nuevo valor.

Yo intente hacer un metodo que la reinicie el scanner para mayor legibilidad, pero o funciona:

//No funciona
public class MiClase{

public Scanner borrar(Scanner s){
s = new Scanner(System.in);
return s;
}
public static void main(...){
MiClase m = new MiClase();
String cadena = s.read();
System.out.print(cadena);
s = m.borrar(s);
}

Pero esto no funcionaría ya que no puedo cambiar la referencia de un objeto desde el interior de otro método, por lo tanto la primera solucion:
s = new Scanner(System.in);

es la que te recomiendo.

Espero esto pueda por ahora ayudarte en tu proyecto, si necesitas mas información, no dudes en escribir que si tengo un rato de tiempo podría ayudarte.

punketo dijo...

Hola, lo que muestra del uso de la clase Scanner es muy basico, quisia ver si describes las demas funcionalidades que tiene...Y buscando mainformacion de esto encontre con pedazo de codigo que usaba la clase Scanner que es el sgte:

public static void main(String[]args){
Scanner s= new Scanner(System.in);
float numero;
String palabra;
for(int i=1; i<=5; i++){
System.out.println("Escribe una palabra;");
palabra=s.nextLine();
System.out.println("Escribe un numero: ");
numero=s.nextFloat();
}
Cuyo problema(posteado por una chica) indicaba qe a la 2da iteracion se saltaba de pedir la cadena y pasaba a pedir directamente el nro. pq?. Encontre dos soluciones para esto.
1era: La creacion de la instancia de la clase scanner debe ser dentro del bucle. Asi
//...
for(int i=1; i<=5; i++){
Scanner s= new Scanner(System.in);
//...
2da:Para leer la cadena no usar el metodo readLine(), sino el read().

Ahora para ambos casos proque es que pasa eso, como es que trabaja realmente la clase Scanner, pq se salta el pedir la cadena, Es donde esperoqque le puedes echar un ojo a esto y des una buena explicacion. Saludos y gracias.

Anónimo dijo...

hola, tal vez este problema no sea tan dificil de resolver pero necesito salir de esta duda: supongamos que escribo un programa que pida al usuario una cadena y este lo lea utilizando Scanner , lo guarda en una variable String y luego muestre la misma cadena, el problema es que la cadena obligatoriamente va a tener espacios pero lo que muestra en pantalla es solo la primera palabra de la cadena. Para explicarme mejor, supongamos que el usuario ingresa lo siguiente:

Cadena con espacios

pero el programa devuelve esto:

Cadena

Necesito saber como incluir toda la cadena (incluyendo los espacios) en la variable que la guarda, y que despues muestre toda la cadena.

Saludos desde Colombia.

Anónimo dijo...

Excelente ayuda lo de las matrices