¿Cuál es la más eficiente y elegante manera de volcar un StringBuilder a un archivo de texto?

Que usted puede hacer:

outputStream.write(stringBuilder.toString().getBytes());

Pero es eficiente para una muy larga archivo?

Hay una manera mejor?

  • Qué tan grande sería decir «muy largo» es? En el orden de KB, MB o más? Será su StringBuilder del tamaño de acercarse a cualquiera de los límites prácticos, como el máximo de memoria asignada a la máquina virtual?
  • Tal vez un par de megabytes…
  • No puedo estar sin el StringBuilder, que es lo que mi API está regresando.
InformationsquelleAutor Patrick | 2009-11-04

9 Comentarios

  1. 40

    Como se ha señalado por otros, el uso de un Escritor, y el uso de un BufferedWriter, pero luego no llame writer.write(stringBuilder.toString()); en lugar de sólo writer.append(stringBuilder);.

    EDIT: Pero, veo que has aceptado una respuesta diferente porque era un one-liner. Pero esa solución tiene dos problemas:

    1. no aceptar un java.nio.Charset. MALO. Siempre se debe especificar un conjunto de Caracteres de forma explícita.

    2. sigue haciendo de sufrir un stringBuilder.toString(). Si la simplicidad es realmente lo que busca, pruebe el siguiente de la La guayaba proyecto:

    Los archivos.escribir(stringBuilder, archivo, tipos de caracteres.UTF_8)

    • escritor.escribir(); no tomar StringBuilder como argumento. Puedo especificar la codificación con FileUtils.writeStringToFile(archivo, cadena, Cadena de codificación)
    • Terriblemente lo siento-que se supone que iba a decir el escritor.append()! La fijación de la misma.
    • También, gracias por aclarar acerca de FileUtils, pero aún así, la especificación de un conjunto de Caracteres de una Cadena es cojo.
    • +1. ¿Hay alguna razón para utilizar un BufferedWriter cuando sólo hacemos una escritura/anexar la llamada?
    • Mirando la fuente de Escritor.append() vemos una llamada de escritura(csq.toString() por lo que todavía se llama a toString() en el generador de cadenas de objeto. Así que nada ganado.
    • +1: Esta es la respuesta correcta.
    • Dado el tiempo y las opiniones, estoy marcar esto como la respuesta correcta ahora. Pero el resto de las opciones a continuación puede todavía ser útil a muchos.

  2. 24

    Debe utilizar un BufferedWriter para optimizar la escribe (siempre de escritura de datos de carácter mediante un Escritor en lugar de un OutputStream). Si usted no fuera la escritura de datos de carácter, usted podría usar un BufferedOutputStream.

    File file = new File("path/to/file.txt");
    BufferedWriter writer = null;
    try {
        writer = new BufferedWriter(new FileWriter(file));
        writer.write(stringBuilder.toString());
    } finally {
        if (writer != null) writer.close();
    }

    o, usando try-con-recursos (Java 7)

    File file = new File("path/to/file.txt");
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
        writer.write(stringBuilder.toString());
    }

    Desde que está en última instancia, de la escritura a un archivo, un mejor enfoque sería para escribir en el BufferedWriter más a menudo en lugar de la creación de una gran StringBuilder en la memoria y la escritura todo al final (dependiendo de su caso de uso, que incluso podría ser capaz de eliminar el StringBuilder completamente). Escrito de forma incremental durante el proceso, guardar la memoria y hacer un mejor uso de su limitado de e/S de ancho de banda, a menos que otro subproceso está tratando de leer una gran cantidad de datos desde el disco al mismo tiempo que estamos escribiendo.

    • Bonita respuesta, gracias. Sin embargo, en aras de la exhaustividad: Usted también puede llamar escritor.flush() y / o escritor.close() para asegurarse de que la Cadena completa que realmente está escrito antes de que el programa o subproceso termina.
    • Buen punto; he actualizado mi respuesta.
    • La primera versión no compilar en Java 5. El escritor no se encuentra en la cláusula finally
    • gracias; debe ser corregido ahora.
    • Gracias por señalar que necesitamos tener ‘escritor.close();’. Me enfrentaba a un problema que no todos mis cadenas se escriben en el archivo y me depurando durante varias horas, pero no puede encontrar la razón. Entonces trato de ‘escritor.close();’, todo funciona perfectamente.
  3. 15

    Bien, si la cadena es enorme, toString().getBytes() va a crear duplicados bytes (2 o 3 veces). El tamaño de la cadena.

    Para evitar esto, se puede extraer la parte de la cadena y escribir en partes separadas.

    Aquí es cómo es posible que se ve:

    final StringBuilder aSB = ...;
    final int    aLength = aSB.length();
    final int    aChunk  = 1024;
    final char[] aChars  = new char[aChunk];
    
    for(int aPosStart = 0; aPosStart < aLength; aPosStart += aChunk) {
        final int aPosEnd = Math.min(aPosStart + aChunk, aLength);
        aSB.getChars(aPosStart, aPosEnd, aChars, 0);                 //Create no new buffer
        final CharArrayReader aCARead = new CharArrayReader(aChars); //Create no new buffer
    
        //This may be slow but it will not create any more buffer (for bytes)
        int aByte;
        while((aByte = aCARead.read()) != -1)
            outputStream.write(aByte);
    }

    Espero que esto ayude.

    • +1 para la mayor parte de la memoria solución eficiente.
    • +1 no Es realmente más lento, sólo probado con un 50 MB de Cadena. Pero lo que realmente ahorra memoria. (aprox. 2MB vs 130MB para los otros métodos)
    • Los «grandes» diferencias de rendimiento vienen de la subyacente OutputStream. En muchos casos la escritura(matriz) llamada al método se descompone en un bucle while internamente. Bonito ejemplo.
    • Es esta memoria más eficiente que el .anexar solución? Me imagino que el escritor puede estar haciendo cosas similares bajo el capó.
    • Ahle: hasta donde yo sé (y probado), append es el todo, si no la más eficiente. Otro que es muy eficiente (para Stream) es write(byte). Java es de código abierto ahora para que pueda ver el código y como recuerdo de la aplicación de anexar y escribir están siempre relacionados.
    • Sí, la acaba, append(CharacterStream cs) = escribir(cs.toString())

  4. 14

    Podría utilizar el Apache Commons IO de la biblioteca, que le da FileUtils:

    FileUtils.writeStringToFile(file, stringBuilder.toString(), Charset.forName("UTF-8"))
    • Estoy elegir esta como la mejor respuesta solo porque los resúmenes de las complicaciones de mí. Dado que podría no ser la más eficiente. Pero las otras respuestas son demasiado grandes, puede utilizar si la eficiencia comienza a sufrir.
    • esto tiene dos grandes problemas que he explicado en mi respuesta.
  5. 3

    De datos de carácter mejor uso Reader/Writer. En su caso, utilizar un BufferedWriter. Si es posible, utilice BufferedWriter desde el principio en lugar de StringBuilder para ahorrar memoria.

    Tenga en cuenta que su manera de llamar la no-arg getBytes() método sería utilizar la plataforma de codificación de caracteres por defecto para decodificar los personajes. Esto puede fallar si la plataforma de codificación predeterminada es, por ejemplo, ISO-8859-1 mientras que su Cadena de datos contiene caracteres fuera de la ISO-8859-1 charset. Un mejor uso de los getBytes(charset) donde puede especificar el conjunto de caracteres del mismo, como la UTF-8.

  6. 1

    Si la cadena es larga, definitivamente debe evitar toString(), que hace una copia de la cadena. La manera más eficiente a escribir para transmitir debe ser algo parecido a esto,

    OutputStreamWriter writer = new OutputStreamWriter(
            new BufferedOutputStream(outputStream), "utf-8");
    
    for (int i = 0; i < sb.length(); i++) {
        writer.write(sb.charAt(i));
    }
    • escritor.append(sb)
    • Por favor, no use escritor.append(sb). Es lo mismo como escritor.escribir(sb.toString()). Así se frustra el propósito.
  7. 1

    Desde java 8 usted sólo necesita hacer esto:

    Files.write(Paths.get("/path/to/file/file_name.extension"), stringBuilder.toString().getBytes());

    Usted no necesita las bibliotecas de terceros para hacerlo.

  8. 0

    Basado en https://stackoverflow.com/a/1677317/980442

    Puedo crear esta función que uso OutputStreamWriter y la write(), esta es la memoria optimizado demasiado, mejor que solo uso StringBuilder.toString().

    public static void stringBuilderToOutputStream(
            StringBuilder sb, OutputStream out, String charsetName, int buffer)
            throws IOException {
        char[] chars = new char[buffer];
        try (OutputStreamWriter writer = new OutputStreamWriter(out, charsetName)) {
            for (int aPosStart = 0; aPosStart < sb.length(); aPosStart += buffer) {
                buffer = Math.min(buffer, sb.length() - aPosStart);
                sb.getChars(aPosStart, aPosStart + buffer, chars, 0);
                writer.write(chars, 0, buffer);
            }
        }
    }
  9. 0

    Puntos de referencia para la mayoría de las respuestas aquí + mejora de la aplicación: https://www.genuitec.com/dump-a-stringbuilder-to-file/

    La implementación final es a lo largo de las líneas de

    try {
        BufferedWriter bw = new BufferedWriter(
                new OutputStreamWriter(
                        new FileOutputStream(file, append), charset), BUFFER_SIZE);
        try {
            final int length = sb.length();
            final char[] chars = new char[BUFFER_SIZE];
            int idxEnd;
            for ( int idxStart=0; idxStart<length; idxStart=idxEnd ) {
                idxEnd = Math.min(idxStart + BUFFER_SIZE, length);
                sb.getChars(idxStart, idxEnd, chars, 0);
                bw.write(chars, 0, idxEnd - idxStart);
            }
            bw.flush();
        } finally {
            bw.close();
        }
    } catch ( IOException ex ) {
        ex.printStackTrace();
    }

Dejar respuesta

Please enter your comment!
Please enter your name here