Каков наиболее эффективный/элегантный способ сброса StringBuilder в текстовый файл?
Вы можете сделать:
outputStream.write(stringBuilder.toString().getBytes());
Но эффективен ли он для очень длинного файла?
Есть ли лучший способ?
Каков наиболее эффективный/элегантный способ сброса StringBuilder в текстовый файл?
Вы можете сделать:
outputStream.write(stringBuilder.toString().getBytes());
Но эффективен ли он для очень длинного файла?
Есть ли лучший способ?
Как указывалось другими, используйте Writer и используйте BufferedWriter, но затем не вызывайте writer.write(stringBuilder.toString());
, а просто writer.append(stringBuilder);
.
EDIT: Но я вижу, что вы приняли другой ответ, потому что это был один лайнер. Но это решение имеет две проблемы:
он не принимает java.nio.Charset
. ПЛОХО. Вы всегда должны явно указывать Charset.
он все еще заставляет вас страдать stringBuilder.toString()
. Если простота в том, что вам нужно, попробуйте следующее из проекта Guava:
Вы должны использовать 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). Запись поэтапно во время обработки будет экономить память и будет лучше использовать вашу ограниченную пропускную способность ввода-вывода, если другой поток не пытается прочитать много данных с диска одновременно с тем, что вы пишете.
Ну, если строка огромна, 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);
}
Надеюсь, что это поможет.
Вы можете использовать библиотеку Apache Commons IO, которая дает вам FileUtils:
FileUtils.writeStringToFile(file, stringBuilder.toString(), Charset.forName("UTF-8"))
Для символьных данных лучше использовать Reader/Writer
. В вашем случае используйте BufferedWriter
. Если возможно, используйте BufferedWriter
с начала вместо StringBuilder
для сохранения памяти.
Обратите внимание, что ваш способ вызова метода non-arg getBytes()
будет использовать кодировку символов по умолчанию для декодирования символов. Это может привести к сбою, если кодировка по умолчанию платформы, например, ISO-8859-1
, в то время как ваши данные String содержат символы вне ISO-8859-1
charset. Лучше используйте getBytes(charset)
, где вы можете указать кодировку самостоятельно, например UTF-8
.
Если сама строка длинна, вам определенно следует избегать toString(), которая создает другую копию строки. Самый эффективный способ записи в поток должен быть примерно таким,
OutputStreamWriter writer = new OutputStreamWriter(
new BufferedOutputStream(outputStream), "utf-8");
for (int i = 0; i < sb.length(); i++) {
writer.write(sb.charAt(i));
}
Начиная с Java 8 вам нужно только сделать это:
Files.write(Paths.get("/path/to/file/file_name.extension"), stringbuilder.toString().getBytes());
Вам не нужны сторонние библиотеки для этого.
На основе fooobar.com/questions/178031/...
Я создаю эту функцию, которая использует 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);
}
}
}
Контрольные показатели для большинства ответов здесь + улучшенная реализация: 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();
}