🇺🇦 Слава Україні! Consulta como ayudar a Ucrania y pide a tu gobierno que se movilice en supportukrainenow.org.

Java IO

InputStreamReader y OutputStreamWriter

Muchas clases de la biblioteca estandar de Java que generan streams, los hacen en modo byte, por lo que si vamos a querer leer o escribir en modo caracter, tendremos que pasar esos streams a través de InputStreamReader y de OutputStreamWriter, que son clases que sirven para hacer la traducción entre un tipo de stream y otro.

Fecha de emisión:

Duración: 6:22

Ver en YouTube

Notas del episodio

Como os contaba, Reader y Writer no tienen muchísima dificultad una vez que ya os he contado todo sobre InputStream y OutputStream. Los métodos son parecidos y la única diferencia está en cómo escribimos o leemos la información. Puede ser más relevante hablar acerca de la traducción entre una categoría de clases y otra. Por ejemplo, cómo convertimos de un InputStream a un Reader, o de un OutputStream a un Writer.

Por ejemplo: imaginemos que queremos leer de System.in. Seguramente, a estas alturas, si habéis hecho un Hola Mundo con consola, tal vez hayáis visto en qué consisten System.in, System.out y System.err. Curiosamente son streams, así que cada vez que haces System.out estás usando un stream de salida, y cada vez que haces System.in estás usando un stream de entrada. Sin embargo, este es un stream de entrada binario, porque como Java no sabe qué vamos a hacer, lo trabaja como si fuese binario, así puedes usarlo para tanto texto como datos. Pero si lo vamos a usar para leer texto, una de las primeras cosas que tendremos que hacer será adaptarlo para que podamos leer como si fuesen cadenas de caracteres. Cosa que por cierto podemos hacer mediante un InputStreamReader.

Es una clase que sirve para hacer una traducción entre un InputStream y un Reader. InputStreamReader acepta como parámetro un InputStream. Por ejemplo, System.in. Y esto lo que nos devuelve es un InputStreamReader, un tipo de Reader con el que podemos leer en forma de cadena de caracteres a partir de un stream de entrada que sea binario, como System.in o la entrada que nos proporciona un socket TCP o algún otro tipo de InputStream que hayamos abierto. En este caso, si lo quisiese utilizar, podría sacarle partido dentro de un bucle try-catch o de un try-with, como siempre. Para hacerlo más útil, voy a hacer un BufferedReader en el cual voy a envolver ese Reader para poder leer de la entrada estandar de forma bufferizada.

Este es un patrón bastante habitual y este es un código que seguramente en muchos hola mundos de Java que tienen que ver con entrada os encontraréis. Una vez tenéis el BufferedReader lo podéis leer, lo podéis usar con parsers para interpretar correctamente lo que se escribe. Yo por ahora únicamente voy a usarlo como stream de entrada. Y lo que voy a hacer es utilizar ese InputStreamReader para transformar mi InputStream en un Reader, para poder hacer la conexión como si fuesen caracteres de lo que por otro lado sólo podrían ser bytes. Voy a ponerle un catch para que no llore, y ahora voy a leer tal cual. De hecho voy a copiarme prácticamente el código que escribí antes para hablar de los Readers, porque así en realidad ya lo tenemos hecho. Lo que tengo que hacer ahora es invocarlo.

Ahora que lo pienso, creo que esto es un poco más complicado porque si yo utilizo la terminal para escribir caracteres, la salida también va a ser igual. Voy a poner el ch que hayamos leído pero voy a ponerle un espacio, y la longitud en caracteres de nuestra cadena. Así por lo menos es un poco diferente, porque de lo contrario vamos a ver ahora que es complicado de comprender cuál es la entrada y cuál es la salida.

Cuando ejecute este programa, van a ocurrir algunas cositas. La consola se va a quedar así, esperando, y según el IDE que estéis usando va a ser diferente, pero notaréis que vuestro panel, en el que normalmente se muestra la salida estandar cuando haceis System.out se va a quedar así: esperando. Lo que está ocurriendo acá es que tenemos la posibilidad de escribir sobre este panel porque estamos leyendo desde System.in, que es la entrada estandar, actualmente conectada a este panel. Así que puedo poner "Hola, buenos días". Y pulsar enter. Lo que ocurrirá cuando pulse Enter, es que esta cadena de texto que he metido se empujará a System.in, y por lo tanto está disponible.

Cuando el programa se queda congelado, es porque tenemos a esto haciendo un read(), que como os contaba al principio de este taller, esperará a que haya datos. Hasta que no haya datos, se va a quedar ahí esperando. Cuando pulsamos Enter, la mayoría de editores empujan esa información al InputStream, y así al haber información, desbloquea automáticamente todos los reads. Esto provoca que esté disponible, el InputStreamReader lo transformará a una cadena de caracteres y el BufferedReader nos permita leerlo. Por eso, aparece este "Hola buenos días 16", que quiere decir que hemos leído 16 caracteres de nuestro buffer, justo lo que acabamos de meter.

Lo podemos tener infinitamente leyendo, hasta que pulsemos Control D. O Control C, o salgamos según el entorno. En algunos es diferente. A veces, en una consola, con pulsar Control D puedes salir, pero aquí lo voy a parar porque no sé cómo se hace en NetBeans.

Sea como sea, mediante InputStreamReader podéis convertir de un InputStream a un Reader. Lo mismo se puede decir, por cierto, en el caso de clases como OutputStreamWriter. Sirve para transformar un OutputStream en un Writer. De esta forma, yo puedo engancharle System.out, que es un OutputStream, pero podría tener una referencia a él como si fuese un Writer, de tal forma que pueda empujarle cadenas de caracteres utilizando, por ejemplo, write("Hola como estamos"), y esto se mande a System.out.

Lo voy a meter en un try, para que haga lo que tiene que hacer. Voy a ponerle un catch. De hecho, voy a comentar este código para que no se ejecute dos veces. Esto en realidad es muy pedante. Solo lo he hecho a modo de ejemplo pero hay clases mejores donde puedes hacer esto. Por ejemplo, si tienes que escribir sobre un socket TCP. Al fin y al cabo, como System.out ya es un PrintStream, tiene la posibilidad de hacer esto sin crear un Writer, pero bueno. Sea como sea, si llamas a write() a través de un Writer que está conectado con un OutputStream, vas a poder hacer la traducción de una cadena de caracteres, que se van a mandar a través de un OutputStream, en este caso de System.out.