TransWikia.com

JAVA Как обработать информацию из файла размером 1-2TB?

Stack Overflow на русском Asked by ForsaiR on January 27, 2021

Имеется задача: в строке файла имеется 3 значения, найти их среднее и вывести минимальное значение из всех строк в файле.
Вся проблема заключается в том – как считать эти данные вообще? Уже при попытке считать пару гигов все крашится и выпадает ошибка нехватки памяти. И возможно ли это сделать это за оптимальное время?

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class Solution {

    public static void main(String[] args) throws Exception {

        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        FileInputStream fileInputStream = new FileInputStream(reader.readLine());

        reader.close();

        int[] arrayPoint = new int[3];
        int size = 65536;

        int bufferSize = size;
        byte buffer[] = new byte[size];

        while (fileInputStream.available() > 0) {
            if (fileInputStream.available() < size) bufferSize = fileInputStream.available();

            fileInputStream.read(buffer, 0, bufferSize);

            byte[] byteArray = new byte[1];
            int byteArraySize = 0;
            byte pointArraySize = 0;

            for(int i = 0; i < bufferSize; i += 1) {
                if (buffer[i] == 0x20) {
                    if (byteArraySize > 0) {
                        try {
                            arrayPoint[pointArraySize] = byteArrayToInt(byteArray);
                            pointArraySize += 1;
                            if (pointArraySize > 3) {
                                search(arrayPoint);
                                arrayPoint = new int[3];
                                pointArraySize = 0;
                            }
                            byteArray = new byte[1];
                            byteArraySize = 0;
                        } catch (Exception e) {
                            byteArray = new byte[1];
                            arrayPoint = new int[3];
                            byteArraySize = 0;
                            pointArraySize = 0;
                        }
                    }
                } else if ((buffer[i] == 0x2D) || ((buffer[i] > 0x2F) && (buffer[i] < 0x3A))) {
                    byteArraySize += 1;
                    //тут возникли проблемы со считыванием нескольких чисел и больших 
                    //в основном с byteArray
                }
            }
        }
    }

    public static void search(int[] points) {
        //тут поиск среднего, в принципе есть, но пока пусть глаза не мазолит
    }

    public static int byteArrayToInt(byte[] byteArray) {
        int value = byteArray[byteArray.length - 1] - 48;
        int count = 10;
        for (int i  = byteArray.length - 2; i >= 0; i -= 1) {
            if (byteArray[i] != 45) {
                value += (byteArray[i] - 48) * count;
                count *= 10;
            } else {
                value *= -1;
            }
        }
        return value;
    }
}

2 Answers

Решение на Java 8 Stream Api

public class Solution {

private static final String SPACE = " ";

public static void main(String[] args) {
    // оборачиваем в try-with-resources чтобы избежать проблем с закрытием Scanner
    try (Scanner scanner = new Scanner(System.in)) {
        // Сканируем одну строку и получаем из нее путь к файлу
        Double minValueFromBufferedReader = getMinValueFromFile(scanner.nextLine());
        //TODO: сделать что-то со значением
    } catch (IOException e) {
        // TODO: ошибки чтения, файл заблокирован, не существует, и т.п.
        e.printStackTrace();
    }
}

private static Double getMinValueFromFile(String filePath) throws IOException {
    // оборачиваем в try-with-resources чтобы избежать проблем с закрытием файла
    try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
        return lines
                //разделяем значения в строке и возвращаем минимальное из них
                .mapToDouble(Solution::splitWithSpaceAndReturnMin)
                //сравниваем минимальные значения строк и возвращаем минимальное из них
                .min()
                //если таких нет - возвращаем максимальное значение
                .orElse(Double.MAX_VALUE);
    }
}

private static Double splitWithSpaceAndReturnMin(String currentLine) {
    // разделяем строку на 3 значения, разделенные пробелами и помещаем в массив
    String[] numbersAsString = currentLine.split(SPACE, 3);
    return Arrays.stream(numbersAsString)
            // превращаем строки в числа с плавающей точкой (Double)
            .mapToDouble(Double::parseDouble)
            //находим среднее из них
            .average()
            //если таких нет - вернуть максимально возможное значение
            .orElse(Double.MAX_VALUE);
}

}

Correct answer by Nalmelune on January 27, 2021

Я бы предложил не заниматься парсингом в ручную и переложить всю работу на стандартную библиотеку.

    double min = Double.MAX_VALUE;

    Scanner r = new Scanner(System.in);
    while (r.hasNextInt()) {
        int a = r.nextInt();
        int b = r.nextInt();
        int c = r.nextInt();

        double average = (a + b + c) / 3.;

        min = Math.min(min, average);
    }

    System.out.println("min = " + min);

Answered by talex on January 27, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP