Pergunta

Qual é a maneira mais eficiente/elegante de despejar um StringBuilder em um arquivo de texto?

Você pode fazer:

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

Mas isso é eficiente para um arquivo muito longo?

Existe uma maneira melhor?

Foi útil?

Solução

Como apontado por outros, use um escritor e use um escritor de buffer, mas depois não ligue writer.write(stringBuilder.toString()); em vez disso writer.append(stringBuilder);.

EDIT: Mas vejo que você aceitou uma resposta diferente porque era uma linha. Mas essa solução tem dois problemas:

  1. não aceita um java.nio.Charset. MAU. Você sempre deve especificar um charset explicitamente.

  2. ainda está fazendo você sofrer um stringBuilder.toString(). Se a simplicidade é realmente o que você procura, tente o seguinte do Goiaba projeto:

Files.write (stringbuilder, arquivo, charsets.utf_8)

Outras dicas

Você deve usar um bufferwriter para otimizar as gravações (sempre escreva dados de caracteres usando um escritor em vez de um outputStream). Se você não estivesse escrevendo dados de caracteres, usaria um bufferoutputStream.

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();
}

Ou, usando o Try-With-Resources (Java 7 ou mais)

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

Como você está escrevendo em um arquivo, uma abordagem melhor seria escrever para o BufferWriter com mais frequência, em vez de criar um enorme StringBuilder na memória e escrever tudo no final (dependendo do seu caso de uso, você pode até poder ser capaz para eliminar completamente o StringBuilder). Escrever incrementalmente durante o processamento salvará a memória e fará melhor uso da sua largura de banda de E/S limitada, a menos que outro thread esteja tentando ler muitos dados do disco ao mesmo tempo em que você está escrevendo.

Bem, se a string for enorme, toString().getBytes() criará bytes duplicados (2 ou 3 vezes). O tamanho da string.

Para evitar isso, você pode extrair pedaços da corda e escrevê -la em peças separadas.

Aqui está como pode parecer:

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);                 // Crie nenhum novo buffer
    final CharArrayReader aCARead = new CharArrayReader(aChars); // Crie nenhum novo buffer

    // isso pode ser lento, mas não criará mais buffer (para bytes)
    int aByte;
    while((aByte = aCARead.read()) != -1)
        outputStream.write(aByte);
}

Espero que isto ajude.

Você poderia usar o Apache Commons io biblioteca, o que te dá FileUtils:

FileUtils.writeStringToFile(file, stringBuilder.toString(), Charset.forName("UTF-8"))

Para dados de caracteres, melhor uso Reader/Writer. No seu caso, use um BufferedWriter. Se possível, use BufferedWriter Desde o início em vez de StringBuilder Para salvar a memória.

Observe que sua maneira de chamar o não-Arg getBytes() O método usaria a codificação de caracteres padrão da plataforma para decodificar os caracteres. Isso pode falhar se a codificação padrão da plataforma for, por exemplo ISO-8859-1 Enquanto os dados da sua string contêm caracteres fora do ISO-8859-1 Charset. Melhor usar o getBytes(charset) onde você pode especificar o charset, como UTF-8.

Se a string em si for longa, você definitivamente deve evitar o ToString (), o que faz outra cópia da string. A maneira mais eficiente de escrever para transmitir deve ser algo assim,

OutputStreamWriter writer = new OutputStreamWriter(
        new BufferedOutputStream(outputStream), "utf-8");

for (int i = 0; i < sb.length(); i++) {
    writer.write(sb.charAt(i));
}

Como Java 8 você só precisa fazer isso:

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

Você não precisa de bibliotecas de terceiros para fazer isso.

Baseado em https://stackoverflow.com/a/1677317/980442

Eu crio essa função que usa OutputStreamWriter e a write(), isso também é otimizado pela memória, melhor do que apenas usar 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);
        }
    }
}

Referências para a maioria das respostas aqui + Implementação aprimorada: https://www.genuitec.com/dump--stringbuilder-to-file/

A implementação final é ao longo das linhas 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();
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top