web-dev-qa-db-ja.com

Java StringBuilderをファイルにダンプする

StringBuilderをテキストファイルにダンプする最も効率的でエレガントな方法は何ですか?

できるよ:

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

しかし、これは非常に長いファイルに対して効率的ですか?

もっと良い方法はありますか?

35
Patrick

他の人が指摘したように、Writerを使用し、BufferedWriterを使用しますが、writer.write(stringBuilder.toString());を呼び出す代わりに、単にwriter.append(stringBuilder);を呼び出します。

編集:しかし、ワンライナーだったので、あなたは別の答えを受け入れたようです。しかし、その解決策には2つの問題があります。

  1. _Java.nio.Charset_は受け入れません。悪い。常に明示的にCharsetを指定する必要があります。

  2. それでもstringBuilder.toString()に苦しんでいます。シンプルさが本当に必要なものである場合は、 Guava プロジェクトから次を試してください。

Files.write(stringBuilder、file、Charsets.UTF_8)

40

BufferedWriterを使用して書き込みを最適化する必要があります(常にOutputStreamではなくWriterを使用して文字データを書き込みます)。文字データを作成していない場合は、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();
}

または、try-with-resourcesを使用(Java 7以降)

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

最終的にファイルに書き込むため、より良いアプローチは、メモリ内に巨大なStringBuilderを作成し、最後にすべてを書き込むのではなく、BufferedWriterに書き込むことです(ユースケースによっては、 StringBuilderを完全に削除します)。処理中にインクリメンタルに書き込むと、メモリを節約し、書き込み中に別のスレッドがディスクから大量のデータを読み取ろうとしない限り、制限されたI/O帯域幅をより有効に活用できます。

24
rob

さて、文字列が巨大な場合、toString().getBytes()は重複バイトを作成します(2または3回)。ストリングのサイズ。

これを回避するには、文字列のチャンクを抽出し、別々の部分に書き込みます。

外観は次のとおりです。

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

お役に立てれば。

15
NawaMan

Apache Commons IO ライブラリを使用すると、 FileUtils が得られます。

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

Java 8なので、これを行う必要があるだけです:

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

そのためにサードパーティのライブラリは必要ありません。

https://stackoverflow.com/a/1677317/980442 に基づく

OutputStreamWriterwrite()を使用するこの関数を作成します。これは、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);
        }
    }
}
0
Daniel De León

ここでのほとんどの回答のベンチマーク+実装の改善: https://www.genuitec.com/dump-a-stringbuilder-to-file/

最終的な実装は、

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