productor - monitores en java
Cuándo usar qué subclase de escritor en Java; prácticas comunes (4)
Siempre he estado ligeramente confundido con la cantidad de implementaciones de IO diferentes en Java, y ahora que estoy completamente estancado en el desarrollo de mi proyecto, me estaba tomando mi tiempo para leer cosas útiles mientras tanto.
Me he dado cuenta de que no hay una comparación amigable para los principiantes (aparte de una breve explicación en la API para la clase Writer ) entre las diferentes subclases de la clase Writer
. Así que pensé que iba a rechazar la pregunta, ¿para qué sirven esas diferentes subclases?
Por ejemplo, generalmente uso un FileWriter
envuelto con un BufferedWriter
para mis salidas a archivos, pero siempre me ha irritado el hecho de que no haya un método similar a println()
, y uno tiene que usar newLine()
cada segunda línea (para hacer la salida humana legible). PrintWriter
tiene el método println()
pero ningún constructor que admita la adición sin embargo ...
Realmente apreciaría si pudieras darme tus dos centavos de tu experiencia, o un buen guía / cómo podría haberte encontrado.
EDIT: Gracias por las respuestas a todos, realmente aprecio la información que se transmite aquí. Es un poco desafortunado que toda la cosa del append()
termine en foco, simplemente lo puso como ejemplo. Mi pregunta se refería principalmente a la necesidad y uso de todas las diferentes implementaciones, que supongo que se mencionó un poco en un par de respuestas.
Es difícil elegir una respuesta como aceptada, ya que hay tres respuestas realmente sólidas, cada una ha contribuido a mi comprensión del problema. Voy a tener que irme con Anon, esta vez ya que él tiene la menor cantidad de representantes. puntos (supongo que es nuevo en SO). Tiene 15 respuestas, algunas de las cuales están muy bien formuladas y 0 preguntas. Buena aportación que diría, y que vale la pena promocionar.
Dicho esto, ColinD y Jay también proporcionaron respuestas realmente buenas y señalaron ideas interesantes. Especialmente el comentario de Jay sobre Java envolviendo automáticamente un BufferedWriter
fue digno de mención. Gracias de nuevo chicos, realmente apreciado!
Las clases java.io
generalmente siguen el patrón de Decorator. Entonces, mientras PrintWriter
no tiene el constructor específico que podrías desear, sí tiene un constructor que toma otro Writer
, por lo que puedes hacer algo como lo siguiente:
FileOutputStream fos = null;
try
{
fos = new FileOutputStream("foo.txt");
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(fos, "UTF-8")));
// do what you want to do
out.flush();
out.close();
}
finally
{
// quietly close the FileOutputStream (see Jakarta Commons IOUtils)
}
Como nota de uso general, siempre desea envolver un Writer de bajo nivel (por ejemplo, FileWriter
o OutputStreamWriter
) en un BufferedWriter
, para minimizar las operaciones reales de IO. Sin embargo, esto significa que debe vaciar y cerrar explícitamente el editor externo para garantizar que todo el contenido esté escrito.
Y luego debe cerrar el Escritor de bajo nivel en un bloque final, para asegurarse de que no pierde recursos.
Editar :
Mirar la respuesta de MForster me hizo echar otro vistazo a la API para FileWriter. Y me di cuenta de que no se necesita un conjunto de caracteres explícito, que es una cosa muy mala. Así que he editado mi fragmento de código para usar un FileOutputStream
en un OutputStreamWriter
que toma un conjunto de caracteres explícito.
PrintWriter no tiene un constructor que tome un parámetro "anexar", pero FileWriter sí lo tiene. Y me parece lógico que ahí es donde pertenece. PrintWriter no sabe si está escribiendo en un archivo, un socket, la consola, una cadena, etc. ¿Qué significaría "anexar" en las escrituras en un socket?
Así que la forma correcta de hacer lo que quieres es simplemente:
PrintWriter out=new PrintWriter(new BufferedWriter(new FileWriter(myfile, append)));
Nota interesante: si envuelve un OutputStream en un PrintWriter, Java inserta automáticamente un BufferedWriter en el medio. Pero si envuelve un escritor en un PrintWriter, no lo hace. Así que nada se gana diciendo:
PrintWriter out=new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(myfile))));
Simplemente deje fuera el BufferedWriter y el OutputStreamWriter, de todos modos los obtendrá de forma gratuita. No tengo idea de si hay alguna buena razón para la inconsistencia.
Es cierto que no puede especificar una codificación de caracteres en un FileWriter como notas de ColinD. No sé que eso lo hace "inaceptable". Casi siempre estoy feliz de aceptar la codificación predeterminada. Quizás si estás usando un idioma que no sea inglés, este es un problema.
La necesidad de envolver Writers o OutputStreams en capas me resultó confusa cuando empecé a usar Java. Pero una vez que lo entiendes, no es gran cosa. Solo tienes que inclinar tu mente hacia el marco de escritura. Cada escritor tiene una función. Piense en ello como, quiero imprimir en un archivo, por lo que necesito envolver un FileWriter en un PrintWriter. O, quiero convertir una secuencia de salida en un escritor, por lo que necesito un OutputStreamWriter. Etc.
O tal vez solo te acostumbras a los que usas todo el tiempo. Descúbrelo una vez y recuerda cómo lo hiciste.
Puedes crear un PrintWriter
como este:
OutputStream os = new FileOutputStream("/tmp/out", true);
PrintWriter writer = new PrintWriter(os);
Edición: la publicación de Anon tiene razón sobre el uso de BufferedWriter
en el medio y la especificación de la codificación.
FileWriter
generalmente no es una clase aceptable para usar. No le permite especificar el Charset
de Charset
que se usará para la escritura, lo que significa que está atascado con cualquiera que sea el conjunto de caracteres predeterminado de la plataforma en la que se está ejecutando. No hace falta decir que esto hace que sea imposible usar el mismo conjunto de caracteres para leer y escribir archivos de texto de manera consistente y puede llevar a datos dañados.
En lugar de utilizar FileWriter
, debe envolver un FileOutputStream
en un OutputStreamWriter
. OutputStreamWriter
le permite especificar un conjunto de caracteres:
File file = ...
OutputStream fileOut = new FileOutputStream(file);
Writer writer = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8"));
Para usar PrintWriter
con lo anterior, simplemente envuelva el BufferedWriter
en un PrintWriter
:
PrintWriter printWriter = new PrintWriter(writer);
También puedes usar el constructor PrintWriter
que toma un File
y el nombre de un conjunto de caracteres:
PrintWriter printWriter = new PrintWriter(file, "UTF-8");
Esto funciona bien para su situación particular, y en realidad hace exactamente lo mismo que el código anterior, pero es bueno saber cómo construirlo envolviendo las distintas partes.
Los otros tipos de Writer
son principalmente para usos especializados:
-
StringWriter
es solo unWriter
que se puede usar para crear unaString
.CharArrayWriter
es el mismo parachar[]
. -
PipedWriter
para canalizar a unPipedReader
.
Editar:
Noté que comentaste otra respuesta sobre la verbosidad de crear un escritor de esta manera. Tenga en cuenta que hay bibliotecas como Guava que ayudan a reducir la verbosidad de las operaciones comunes. Tome, por ejemplo, escribir una String
en un archivo en un conjunto de caracteres específico. Con guava solo puedes escribir:
Files.write(text, file, Charsets.UTF_8);
También puedes crear un BufferedWriter
como este:
BufferedWriter writer = Files.newWriter(file, Charsets.UTF_8);