Оптимизация кода, считывание данных построчно
помогите оптимизировать код. Мой код работает очень медленно на больших объемах данных. Проблема: У меня есть входящий файл со временем работы сервера, мне нужно достать из него данные, когда сервер не работал и записать их в другой файл. Старшие товарищи рекомендуют считывать файл построчно и обрабатывать его построчно. Я же, видимо, обрабатываю весь файл сразу, что очень сказывается на производительности. Помогите разобраться в вопросе, что я делаю не так? Заранее благодарю)
public static void unavailable(String source, String target) {
try (BufferedReader reader = new BufferedReader(new FileReader(source));
PrintWriter writer = new PrintWriter(new BufferedOutputStream(new FileOutputStream(target)))) {
String serverDown = null;
while (reader.ready()) {
String status = reader.readLine();
if (serverDown == null && (status.startsWith("400") || status.startsWith("500"))) {
writer.println(status.split(" ")[1] + " server down");
serverDown = status;
} else if (serverDown != null && (!status.startsWith("400") || !status.startsWith("500"))) {
writer.println(status.split(" ")[1] + " server up");
serverDown = null;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
Ответы (3 шт):
Если я правильно понял ваш вопрос, то ниже фрагмент кода, который предназначен для чтения файла построчно с помощью Stream API.
{
List<String> rows = Files.lines(Paths.get(/*source path*/))
.collect(Collectors.toList());
rows.forEach(/*performs the needed operations*/);
}
Как вариант:
package com.lampa;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.concurrent.*;
public class SuperApp {
public static void main(String... args) throws IOException, InterruptedException {
long startTime = System.nanoTime();
//App.run(args);
// Размер читаемого чанка из файла
final int chunkSize = 512 * 1024 * 50;
// Размер чанка для поиска переноса
final int breakLineFindBlockSize = 512 * 1024;
// Количество одновременно работающих потоков поиска по слову
// Блокировка потоков чтения от переполнения памяти.
// Потребление программы == chunkSize * threadSize
final int threadSize = 16;
final Semaphore lock = new Semaphore(threadSize);
final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadSize);
File input = new File("C:\\javafx\\warn.txt");
File output = new File("C:\\javafx\\warn2.txt");
PrintWriter writer = new PrintWriter(new BufferedOutputStream(new FileOutputStream(output)));
RandomAccessFile randomAccessFile = new RandomAccessFile(input, "r");
FileChannel fc = randomAccessFile.getChannel();
long correct = 0; //
long fileSize = input.length();
ByteBuffer chunkBuffer;
ByteBuffer bufferLineFind;
long finalSize = 0;
// Начинаем читать файл в чанки
while (correct < fileSize) {
ByteArrayOutputStream finalBuffer = new ByteArrayOutputStream();
chunkBuffer = ByteBuffer.allocate((correct + chunkSize > fileSize) ? (int) (fileSize - correct) : chunkSize);
fc.position(correct);
fc.read(chunkBuffer);
finalBuffer.write(chunkBuffer.array());
correct += chunkBuffer.capacity();
// Выполняем поиск переноса в чанке поиска переноса
loop: while (correct < fileSize) {
bufferLineFind = ByteBuffer.allocate(breakLineFindBlockSize); //new byte[breakLineFindBlockSize];
fc.position(correct);
fc.read(bufferLineFind);
for (int g = 0; g < breakLineFindBlockSize; g++) {
if (bufferLineFind.get(g) == '\n') {
correct += g;
byte[] array = new byte[g];
System.arraycopy(bufferLineFind.array(), 0, array, 0, g);
finalBuffer.write(array);
break loop;
}
}
correct += breakLineFindBlockSize;
finalBuffer.write(bufferLineFind.array());
}
// Реализация макс количества потоков
lock.acquire();
executor.execute(() -> {
try {
//File newFile = new File("C:\\javafx\\application_" + finalBuffer.size());
//Files.write(newFile.toPath(), finalBuffer.toByteArray());
BufferedReader bfReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(finalBuffer.toByteArray())));
String temp = null;
while ((temp = bfReader.readLine()) != null) {
if (temp.contains("WARN")) {
writer.write(temp + "\n");
}
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
//System.out.println("chunk size: " + finalBuffer.size());
lock.release();
}
});
finalSize += finalBuffer.size();
}
if (executor.getActiveCount() > 0) {
executor.shutdown();
executor.awaitTermination(60, TimeUnit.SECONDS);
}
writer.close();
System.out.println("final : " + finalSize + ":" + input.length());
long endTime = System.nanoTime();
long totalTime = endTime - startTime;
System.out.println("Execution time is " + TimeUnit.NANOSECONDS.toMillis(totalTime) + "ms");
}
}
В итоге оказалось, что надо было развести потоки по разным методам, что бы поток BufferedReader не висел открытым. Метод toFile записывает в файл уже обработанный список, в котором находятся только те данные, которые нам нужны не перегружая при этом систему. Всем спасибо за помощь. Не обращайте внимания на имя класса, это сделано намеренно, что бы избежать дубликатов в проекте)
Вот код который приняли:
public class Analizy {
private static List<String> resultList = new ArrayList<>();
/**
* Method "toFile()" - implements writing the list to a file
* @param target - path to the file to write
*/
private static void toFile(String target) {
try (PrintWriter writer = new PrintWriter(
new BufferedOutputStream(
new FileOutputStream(target)
))) {
resultList.forEach(data -> writer.write(data + System.lineSeparator()));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Server unavailability statistics
*
* @param source data provider file address
* @param target file address for recording statistics
*/
public static void unavailable(String source, String target) throws IOException {
String separate = System.lineSeparator();
StringBuilder build = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(source))) {
String line = reader.readLine();
while (line != null) {
if (line.startsWith("400") || line.startsWith("500")) {
build.append(line.substring(4)).append(" - ");
resultList.add(line.split(" ")[1] + " server down");
while (line.startsWith("400") || line.startsWith("500") || line.isEmpty()) {
line = reader.readLine();
}
build.append(line.substring(4)).append(separate);
resultList.add(line.split(" ")[1] + " server up");
}
line = reader.readLine();
build = new StringBuilder();
}
}
toFile(target);
}
}