Foros del Web » Programación para mayores de 30 ;) » Java »

[SOLUCIONADO] Problema para entender InputStream y los Reader para leer por teclado

Estas en el tema de Problema para entender InputStream y los Reader para leer por teclado en el foro de Java en Foros del Web. Hola a todos. Nos están explicando en clase el tema del inputStream y los Reader, para cómo leer por teclado, pero tras mucho desgañitarme y ...
  #1 (permalink)  
Antiguo 08/04/2016, 16:19
Avatar de BramSt  
Fecha de Ingreso: abril-2015
Mensajes: 117
Antigüedad: 9 años, 8 meses
Puntos: 5
Pregunta Problema para entender InputStream y los Reader para leer por teclado

Hola a todos.



Nos están explicando en clase el tema del inputStream y los Reader, para cómo leer por teclado, pero tras mucho desgañitarme y tratar de entenderlo con tutoriales y por mi cuenta, no consigo aclararme, así que así que a ver si podeis ayudarme:


La manera (sin usar Scanner) que se utiliza para leer por teclado y mostrar una cadena en pantalla es la siguiente:

Código Java:
Ver original
  1.  
  2.  
  3. try
  4.         {
  5.             String cadena = br.readLine();
  6.             System.out.println("cadena: " + cadena);
  7.             System.out.println("br directo: " + br.readLine());
  8.         } catch (IOException e)
  9.         {
  10.            
  11.             e.printStackTrace();
  12.         }


Hasta aquí todo correcto, ¿no?

Bueno, veis en el código que dentro del try, he hecho dos System.out, uno pasando la cadena y otro directamente br.readLine()

Lo que me tiene un poco loco es que, en system.out que pasamos la cadena, si pongo “Hola”, sólo devuelve el texto”cadena: ola”.

Mientras que en el que pasamos el br.readLine(), muestra correctamente la cadena que hayamos introducido por teclado. Ej.”br directo: hola”.

Y las dudas son:

-¿Por qué la cadena “recoge” todos los caracteres menos el primero, que es el que además se supone que pasamos mediante System.in al crear el objeto isr? ¿No se supone que InputStream sólo es capaz de leer un carácter, o mejor dicho, su equivalente en ASCII?

¿Por qué tengo que almacenar lo que devuelva br.readLine() en una variable de tipo String (cadena), y sobre todo, por qué al hacer System.out.println no muestra lo mismo si pasamos la variable cadena que si pasamos directamente el br.readLine()?


Lo digo porque hasta ahora siempre podía pasar al System.out.println tanto una variable como una operación aritmética como un método y no habia pegas. O sea, es lo mismo hacer:

Código Java:
Ver original
  1. int a=2+2;
  2.        
  3.     System.out.println(a);

que hacer:

Código Java:
Ver original
  1. System.out.println(2+2);

O es lo mismo hacer:

Código Java:
Ver original
  1. String b=Character.toString('b');
  2.        
  3.     System.out.println(b);

que hacer:

Código Java:
Ver original
  1. System.out.println(Character.toString('b'));

Por eso me tiene un poco (muy) desconcertado todo esto. Es extrañísimo (para mí al menos) y no sé por dónde agarrarlo



Muchas gracias por adelantado.
  #2 (permalink)  
Antiguo 08/04/2016, 17:58
Avatar de Xerelo  
Fecha de Ingreso: mayo-2009
Mensajes: 2.175
Antigüedad: 15 años, 7 meses
Puntos: 306
Respuesta: Problema para entender InputStream y los Reader para leer por teclado

Cita:
¿No se supone que InputStream sólo es capaz de leer un carácter, o mejor dicho, su equivalente en ASCII?
Por eso estás usando BufferedReader

https://docs.oracle.com/javase/7/doc...redReader.html


Cita:
Por qué tengo que almacenar lo que devuelva br.readLine() en una variable de tipo String (cadena)
No es obligatorio, sólo lo es si vas a hacer más de una operación con el texto, porque de un Stream sólo se puede leer una vez.


Cita:
por qué al hacer System.out.println no muestra lo mismo si pasamos la variable cadena que si pasamos directamente el br.readLine()?
Muestra lo que le pases, creo que tù piensas que con br.readLine() lees dos veces una línea, pero en realidad cada vez que llamas a una readLine lees una nueva línea, recuerda, un byte de un Stream sólo se puede leer una vez. Si haces dos readLine, tienes que esxribir dos veces texto en consola

Cita:
Lo que me tiene un poco loco es que, en system.out que pasamos la cadena, si pongo “Hola”, sólo devuelve el texto”cadena: ola”.
Con ese código imposible que pase lo que dices

Cita:
Hola
cadena: Hola
hola
br directo: hola
__________________
Cada vez que solucionas los problemas de alguien que no se esfuerza, piensa en que el día de mañana puede llegar a ser tu compañero de trabajo, o peor, tu jefe.
  #3 (permalink)  
Antiguo 08/04/2016, 18:51
Avatar de BramSt  
Fecha de Ingreso: abril-2015
Mensajes: 117
Antigüedad: 9 años, 8 meses
Puntos: 5
Respuesta: Problema para entender InputStream y los Reader para leer por teclado

Hola Xerelo, gracias por tu respuesta.



Cita:
Iniciado por Xerelo Ver Mensaje
Por eso estás usando BufferedReader

https://docs.oracle.com/javase/7/doc...redReader.html
Ya, si todo eso ya me lo he mirado, lo que pasa que no lo entiendo a colación del "ola" que aparece después.

Cita:
No es obligatorio, sólo lo es si vas a hacer más de una operación con el texto, porque de un Stream sólo se puede leer una vez.

Cita:
Muestra lo que le pases, creo que tù piensas que con br.readLine() lees dos veces una línea, pero en realidad cada vez que llamas a una readLine lees una nueva línea, recuerda, un byte de un Stream sólo se puede leer una vez. Si haces dos readLine, tienes que esxribir dos veces texto en consola
De hecho creo que por aquí está la clave de que aparezca "ola", y te digo por qué:


Resulta que en mi código (yo no lo consideré importante, y como bien dices, en el código que os pasé resulta que no sucede), había más instrucciones, y bueno, voy a poner el código entero que había, También dejo los comentarios que tiene mi programa por si veis algún sitio donde esté fallando mi razonamiento y de ahí los problemas.

Código Java:
Ver original
  1. package paquetemain;
  2.  
  3. import java.io.BufferedReader;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6.  
  7. public class Main
  8. {
  9.  
  10.     /**
  11.      * @param args
  12.      */
  13.     public static void main(String[] args)
  14.     {
  15.         /*primero, vamos a entender qué hace el System.in.read().
  16.          * Este método es de la clase InputStream (el cual es a su vez llamado   por un objeto -in- de la clase InputStream),
  17.          * lo cuál quiere decir que lee sólo bytes (la unidad mínima), o sea, el código ASCII
  18.          * de ese caracter o lo que sea. Es por tanto un entero lo que nos devuelve. DE A,65 por ejemplo.
  19.          *  */
  20.        
  21.         /*----USO DE LA CLASE INPUTSTREAM(SOLO LEE BYTES)---**/
  22.         int b=0;
  23.        
  24.         try
  25.         {
  26.             System.out.println("almacenar el System.in.read() en variable entera b y mostrarla: (muestra sólo el" +
  27.             " ASCII del primer caracter que metamos: ");
  28.             b=System.in.read(); /*EL READ SE QUEDA ESPERANDO A QUE METAS ALGO POR TECLADO*/
  29.         } catch (IOException e)
  30.         {
  31.             // TODO Auto-generated catch block
  32.             System.out.println("Error");
  33.         }
  34.        
  35.         /*esto ahora nos devoveria 65 (el read queda esperando a que introduzcas el caracter por pantalla).
  36.          * ojo que solo lee un caracter, asi que si pones "hola", solo te pone 104, que es el ascii de h
  37.          * */
  38.        
  39.        
  40.         System.out.println("b " + b);
  41.        
  42.         /*pero necesitamos que si metemos A, lo que nos lea es A, y no 65. ¿Cómo?
  43.          * Pues hay que usar la clase READER, que ya tiene métodos que leen caracteres.
  44.          *
  45.          * DENTRO DE LOS READER HAY DOS CLASES IMPORTANTES: INPUTSTREAMREADER Y BUFFEREDREADER
  46.           */
  47.        
  48.         /*----USO DE LA CLASE INPUTSTREAMREADER (CONVIERTE EL INPUT STREAM EN READER)----*/
  49.        
  50.        
  51.         /*hay que usar un objeto de la clase inputstreamreader y pasarle nuestro objeto InputStream (en este caso, System.in)
  52.          
  53.          */
  54.        
  55.        
  56.         InputStreamReader isr=new InputStreamReader(System.in);
  57.  
  58.        
  59.        
  60.        
  61.         BufferedReader br = new BufferedReader(isr);
  62.        
  63.        
  64.         try
  65.         {
  66.            
  67.             String cadena = br.readLine(); /*espera el texto por teclado*/
  68.             System.out.println("cadena " + cadena);
  69.             System.out.println("br directo " + br.readLine());
  70.  
  71.         } catch (IOException e)
  72.         {
  73.             // TODO Auto-generated catch block
  74.             e.printStackTrace();
  75.         }
  76.  
  77.     }

¿Puede estar el problema en que yo usaba al principio un System.in.read(), y como tú dices, un byte de un Stream solo se puede leer una vez, y para cuando el "Hola" llega a la cadena, la h ya se ha leído y por eso no aparece?

Por más que miro la documentación de oracle, no entiendo bien lo que hace el método read() entonces, ni qué hace después el System.in (o qué contiene), cuando lo pasas por parámetro aquí:

InputStreamReader isr=new InputStreamReader(System.in);

Máxime teniendo aún en la cabeza la idea (quizá errónea) de que un objeto de tipo InputStream como "in" solo lee un byte, o sea, el ASCII de un caracter... pero todavía hay otros tres ("ola"), por aquí...

Creo que toca leer también un poquito sobre los Streams, no obstante.
  #4 (permalink)  
Antiguo 08/04/2016, 23:14
Avatar de chuidiang
Colaborador
 
Fecha de Ingreso: octubre-2004
Mensajes: 3.774
Antigüedad: 20 años, 2 meses
Puntos: 454
Respuesta: Problema para entender InputStream y los Reader para leer por teclado

Hola:

En un InputStream se leen bytes y solo se leen una vez. Tienes el método read() que lee un byte y el metodo read(byte[]) que te permite leer varios de golpe.

Si en el InputStream no hay nada disponible, los métodos read() se quedan bloqueados hasta que haya algo disponible y lo leen. De ahí que tu read() de espera sea el que se "come" la primera letra, la H. La intención del método read() no es esperar a que haya algo disponible, sino leer un byte. El quedarse esperando es un "efecto secundario" de que no haya bytes disponbiles para leer.

Como leer bytes es un poco rollo, es habitual meter ese InputStream dentro de otra clase que tenga métodos más amigables para leer, como el BufferedReader, que tiene (entre otros) un método readLine(), que lee una cadena de texto hasta un retorno de carro y devuelve un String. Este BufferedReader.readLine() se encarga de llamar a los métodos read() de InputStream, leer los bytes que correspondan, convertirlos a String y pasártelo. Es este BufferedReader.readLine() el que decide cuantos bytes leer y lo que hace es leer bytes hasta que encuentra un retorno de carro. Como este método llama a los read(), también se quedaría a la espera de que haya algo disponible para leer en caso de que no lo haya.

Se bueno.
__________________
Apuntes Java
Wiki de Programación
  #5 (permalink)  
Antiguo 09/04/2016, 09:19
Avatar de BramSt  
Fecha de Ingreso: abril-2015
Mensajes: 117
Antigüedad: 9 años, 8 meses
Puntos: 5
Respuesta: Problema para entender InputStream y los Reader para leer por teclado

Muchas gracias por tu excelente explicación, chuidiang.

Entonces, a ver si he comprendido:

System.in es un objeto de tipo InputStream que recoge cualquier cosa que yo meta por teclado pero que sólo lee bytes,¿no? ¿Esto incluye varios caracteres y espacios y lo que sea, hasta que apriete intro?

Por otra parte lo que a mí me ocurría entonces es que, al introducir "hola", yo ya había leído o procesado uno de esos bytes (el de la h), a través del método read, y por eso los únicos que llegaban hasta el inputStreamReader isr para ser pasados a través de System.in como parámetro, eran los restantes ("ola").

Y por lo que tú explicas, a veces se me quedaba el programa "en espera", y otras veces terminaba... porque todo dependía de si quedaban bytes/caracteres por procesar/leer o no, cuando llegaba al último readLine().

¿Estoy en lo cierto?

Muchísimas gracias.
  #6 (permalink)  
Antiguo 09/04/2016, 09:30
Avatar de chuidiang
Colaborador
 
Fecha de Ingreso: octubre-2004
Mensajes: 3.774
Antigüedad: 20 años, 2 meses
Puntos: 454
Respuesta: Problema para entender InputStream y los Reader para leer por teclado

Hola:

Sí, es correcto. Un detalle nada más, un readLine() lee bytes hasta que encuentre un retorno de carro. El readLine() también se queda bloqueado, aunque haya bytes disponibles, hasta que escribas un retorno de carro.

Se bueno.
__________________
Apuntes Java
Wiki de Programación
  #7 (permalink)  
Antiguo 09/04/2016, 15:11
Avatar de BramSt  
Fecha de Ingreso: abril-2015
Mensajes: 117
Antigüedad: 9 años, 8 meses
Puntos: 5
Respuesta: Problema para entender InputStream y los Reader para leer por teclado

Cita:
Iniciado por chuidiang Ver Mensaje
Hola:

Sí, es correcto. Un detalle nada más, un readLine() lee bytes hasta que encuentre un retorno de carro. El readLine() también se queda bloqueado, aunque haya bytes disponibles, hasta que escribas un retorno de carro.

Se bueno.
Muchas gracias Chuidiang. Creo que lo tengo más o menos pillado. Lo único que no entiendo es eso último que me dices... ¿Te refieres a que hasta que no pulse intro, el método readLine() no empieza a leer?

Por otro lado, ¿los Readers no tienen pues la capacidad de leer caracteres en vez de bytes? Lo digo por lo que me comentas de que un readLine() lee bytes.

Un saludo.
  #8 (permalink)  
Antiguo 10/04/2016, 04:12
Avatar de chuidiang
Colaborador
 
Fecha de Ingreso: octubre-2004
Mensajes: 3.774
Antigüedad: 20 años, 2 meses
Puntos: 454
Respuesta: Problema para entender InputStream y los Reader para leer por teclado

Hola:

Sí, readLine() va leyendo todo lo que encuentre, pero el método no te devuelve nada y se queda bloqueado hasta que lea un retorno de carro (es decir, hasta que readline lea uno/dos bytes que correspondan a un retorno de carro, cuyos valores son el 10 y/o el 13). Cuando lee el retorno de carro, convierte los bytes leídos hasta ese momento en cadena de texto (excluido el retorno de carro) y te lo devuelve.

Los reader tiene métodos para leer caracteres, pero esas clases reader, por debajo, leen bytes, los conviertes a caracteres (teniendo en cuenta la codificación del idioma que uses) y es lo que te devuelven.

Otro ejemplo, la clase DataInputStream https://docs.oracle.com/javase/7/doc...putStream.html , tiene métodos para leer enteros, float, etc. Un entero son 4 bytes. La clase, por debajo, lee cuatro bytes, los convierte a un valor entero, y es lo que te devuelve. Posiblemente la llamada a readInt() se quede bloquedada hasta que haya podido leer 4 bytes. Ojo, un entero son cuatro bytes y cada byte no es una cifra. Por ejemplo, 0xff 0xff 0xff 0xff suele ser el valor entero -1. y 0x00 0x00 0x00 0x11 puede ser el 17.

Resumiendo, la base es InputStream que lee bytes, que es lo que realmente hay en cualquier tipo de entrada (fichero, teclado, socket). El resto de clases son "utilidades" para convertir esos bytes en algo útil (caracteres, cadenas de texto, valores enteros, objetos java como la clase ObjectInputStream, etc).

De la misma forma, OutputStream es la base y escribe bytes, que es lo que realmente se envía a cualquier tipo de salida (fichero, consola, socket) y el resto de clases Output son "utilidades" para que tu puedas escribir algo con sentido (caracteres, cadenas de texto, objetos java, enteros, flotantes, etc) y ellas lo convierten a los bytes que correspondan y los envían.

Evidentemente, el que escribe y el que lee deben estar de acuerdo en qué están enviando/recibiendo. Si uno envía un entero, se convierte a bytes (cuatro bytes) y el otro intenta leerlos como cadena de texto, puede haber (y hay) un problema.

Se bueno.
__________________
Apuntes Java
Wiki de Programación
  #9 (permalink)  
Antiguo 16/04/2016, 15:33
Avatar de BramSt  
Fecha de Ingreso: abril-2015
Mensajes: 117
Antigüedad: 9 años, 8 meses
Puntos: 5
Respuesta: Problema para entender InputStream y los Reader para leer por teclado

Muchas gracias, Chuidiang, y perdona por el retraso en contestar.

El retorno de carro es lo mismo que pulsar intro, ¿verdad?

Un saludo. Marco de todos modos el tema como solucionado (ahora ye no tengo mucho tiempo para mirar esto, ya que estamos con las herencias jeje).

Un saludo!

Etiquetas: clase, entender, reader, string, teclado
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 01:30.