43

将 StringBuilder 转储到文本文件的最有效/优雅的方法是什么?

你可以做:

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

但这对很长的文件有效吗?

有没有更好的办法?

4

9 回答 9

46

正如其他人指出的那样,使用 Writer,并使用 BufferedWriter,但不要调用writer.write(stringBuilder.toString());只是writer.append(stringBuilder);.

编辑:但是,我看到您接受了不同的答案,因为它是单行的。但该解决方案有两个问题:

  1. 它不接受java.nio.Charset. 坏的。您应该始终明确指定 Charset。

  2. 它仍然让你受苦stringBuilder.toString()。如果您真的想要简单,请尝试Guava项目中的以下操作:

Files.write(stringBuilder,文件,Charsets.UTF_8)

于 2009-11-05T00:38:56.353 回答
30

您应该使用 BufferedWriter 来优化写入(始终使用 Writer 而不是 OutputStream 写入字符数据)。如果您不写入字符数据,您将使用 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());
}

由于您最终要写入文件,因此更好的方法是更频繁地写入 BufferedWriter 而不是在内存中创建一个巨大的 StringBuilder 并在最后写入所有内容(取决于您的用例,您甚至可以完全消除 StringBuilder)。在处理过程中增量写入将节省内存并更好地利用有限的 I/O 带宽,除非另一个线程在您写入的同时尝试从磁盘读取大量数据。

于 2009-11-04T22:49:52.500 回答
19

您可以使用Apache Commons IO库,它为您提供FileUtils

FileUtils.writeStringToFile(file, stringBuilder.toString(), Charset.forName("UTF-8"))
于 2009-11-04T23:01:52.493 回答
16

好吧,如果字符串很大,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);
}

希望这可以帮助。

于 2009-11-04T23:08:59.130 回答
4

从 java 8 开始,您只需要这样做:

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

您不需要任何第三方库来执行此操作。

于 2019-02-27T16:35:28.780 回答
3

对于字符数据更好地使用Reader/Writer. 在您的情况下,请使用BufferedWriter. 如果可能,BufferedWriter请从头开始使用,而不是StringBuilder节省内存。

请注意,您调用非 arggetBytes()方法的方式将使用平台默认字符编码来解码字符。如果平台默认编码是例如ISO-8859-1当您的字符串数据包含字符集之外的ISO-8859-1字符时,这可能会失败。最好使用getBytes(charset)where 你可以自己指定字符集,例如UTF-8.

于 2009-11-04T23:02:01.290 回答
1

如果字符串本身很长,则绝对应该避免使用 toString(),它会生成另一个字符串副本。写入流的最有效方法应该是这样的,

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

for (int i = 0; i < sb.length(); i++) {
    writer.write(sb.charAt(i));
}
于 2009-11-05T02:38:03.947 回答
1

大多数答案的基准+改进的实施: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();
}
于 2017-12-20T17:40:22.950 回答
0

基于https://stackoverflow.com/a/1677317/980442

我创建了这个使用OutputStreamWriter和的函数write(),这也是内存优化,比仅仅使用更好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);
        }
    }
}
于 2016-10-06T18:32:55.753 回答