Разбор текстовых файлов в Java - PullRequest
13 голосов
/ 21 мая 2009

Я читаю в текстовом файле, используя FileInputStream, который помещает содержимое файла в байтовый массив. Затем я преобразую байтовый массив в строку, используя новую строку (байт).

Когда у меня есть строка, я использую String.split("\n"), чтобы разбить файл на массив строк, а затем взять этот массив строк и проанализировать его, выполнив String.split(",") и удерживая содержимое в Arraylist.

У меня есть файл 200MB + , и ему не хватает памяти, когда я запускаю JVM с 1 ГБ памяти. Я знаю, что где-то правильно что-то делаю, я просто не уверен, что мой метод синтаксического анализа неправильный или структура данных, которую я использую.

У меня уходит также около 12 секунд, чтобы проанализировать файл, который выглядит как много времени. Кто-нибудь может указать на то, что я могу делать, из-за чего у меня заканчивается память и что может вызывать медленную работу моей программы?

Содержимое файла выглядит так, как показано ниже:

"12334", "100", "1.233", "TEST", "TEXT", "1234"
"12334", "100", "1.233", "TEST", "TEXT", "1234"
.
.
.
"12334", "100", "1.233", "TEST", "TEXT", "1234"

Спасибо

Ответы [ 6 ]

11 голосов
/ 21 мая 2009

Я не уверен, насколько это эффективно с точки зрения памяти, но мой первый подход - использовать Сканер , поскольку он невероятно прост в использовании:

File file = new File("/path/to/my/file.txt");
Scanner input = new Scanner(file);

while(input.hasNext()) {
    String nextToken = input.next();
    //or to process line by line
    String nextLine = input.nextLine();
}

input.close();

Проверьте API, как изменить разделитель, который он использует для разделения токенов.

9 голосов
/ 21 мая 2009

Звучит так, будто вы делаете со мной что-то не так - происходит создание целого лотоса-объекта.

Насколько репрезентативен этот "тестовый" файл? Что вы действительно делаете с этими данными? Если это типично для того, что у вас есть, я бы сказал, что в этих данных много повторений.

Если все равно все будет в строках, начните с BufferedReader для чтения каждой строки. Предварительно выделите этот список под размер, близкий к тому, что вам нужно, чтобы не тратить ресурсы на добавление в него каждый раз. Разделите каждую из этих строк на запятую; не забудьте удалить двойные кавычки.

Вы можете спросить себя: "Зачем мне весь этот файл в памяти все сразу?" Можете ли вы немного почитать, немного обработать, и никогда не иметь все это в памяти сразу? Только вы знаете свою проблему достаточно хорошо, чтобы ответить.

Может быть, вы можете запустить jvisualvm, если у вас JDK 6 и посмотреть, что происходит с памятью. Это было бы отличной подсказкой.

5 голосов
/ 19 октября 2011

Посмотрите на эти страницы. Они содержат много парсеров CSV с открытым исходным кодом. JSaPar является одним из них.

4 голосов
/ 21 мая 2009

Похоже, что в данный момент у вас есть 3 копии всего файла в памяти: байтовый массив, строка и массив строк.

Вместо чтения байтов в байтовый массив и последующего преобразования в символы с помощью new String() было бы лучше использовать InputStreamReader, который будет преобразовывать в символы постепенно, а не все заранее.

Кроме того, вместо того чтобы использовать String.split ("\ n") для получения отдельных строк, вы должны читать по одной строке за раз. Вы можете использовать метод readLine() в BufferedReader.

Попробуйте что-то вроде этого:

BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream, "UTF-8"));
try {
  while (true) {
    String line = reader.readLine();
    if (line == null) break;
    String[] fields = line.split(",");
    // process fields here
  }
} finally {
  reader.close();
}
2 голосов
/ 21 мая 2009

Если у вас есть 200 000 000 файлов символов и разделите их на каждые пять символов, у вас будет 40 000 000 String объектов. Предположим, что они обмениваются фактическими символьными данными с исходными 400 МБ String (char - 2 байта). String, скажем, 32 байта, то есть 1 280 000 000 байтов String объектов.

(Вероятно, стоит отметить, что это сильно зависит от реализации. split может создавать полностью строки с полностью новой поддержкой char[] или, OTOH, иметь некоторые общие String значения. Некоторые реализации Java не используют нарезку char[]. Некоторые могут использовать UTF-8-подобную компактную форму и иметь очень плохое время произвольного доступа.)

Даже если принять более длинные строки, это много объектов. С таким большим количеством данных вы, вероятно, захотите работать с большей частью в компактной форме, как оригинал (только с индексами). Преобразуйте в объекты только то, что вам нужно. Реализация должна быть похожа на базу данных (хотя они традиционно не обрабатывают строки переменной длины эффективно).

0 голосов
/ 22 мая 2014

При вызове / вызове вашей программы вы можете использовать эту команду: java [-options] className [args ...]
вместо [-опций] предоставьте больше памяти, например -Xmx1024m или больше. но это всего лишь обходной путь, вам нужно изменить механизм синтаксического анализа.

...