2015-12-29 2 views
1

Я хочу прочитать ASCII-файл, состоящий из «блоков», которые ограничены начальными и конечными тегами.Как читать текстовый файл по блоку с потоками Java 8

Я никогда не использовал потоки Java 8, и я бы хотел протестировать их в этом файловом читателе, но я действительно не знаю, как это сделать.

Для простоты, давайте рассмотрим следующий формат файла (фактический формат файла can be found here):

$Node 
6 
1 1.0 0.0 0.0 
2 -1.0 0.0 0.0 
3 0.0 1.0 0.0 
4 0.0 -1.0 0.0 
5 0.0 0.0 1.0 
6 0.0 0.0 -1.0 
$EndNode 
$Elements 
3 
1 10 1 2 3 
2 10 4 5 6 
3 10 1 5 3 
$EndElements 

Если первая строка каждого блока является количество элементов в блоке. Затем каждый блок представляет собой список значений, разделенных пробелами. Количество значений и типов зависит от блока.

В реальной жизни файл может стать довольно большим (несколько сот Мб, возможно, до нескольких ГБ), поэтому производительность имеет решающее значение.

Использование Java NIO 2 (без Java 8 потоков), я бы сделал что-то вроде этого:

BufferedReader reader = Files.newBufferedReader(filePath, Charset.defaultCharset()); 
String line = null; 
Parser currentParser = defaultParser; 
while ((line = reader.readLine()) != null) { 
    if (line.startsWith("$")) { 
     currentParser = getParser(line); 
     continue; 
    } 
    currentParser.parseLine(line); 
} 

с линией парсер, который был бы достаточно умен, чтобы иметь дело с первой строки блока по-разному что остальное (без необходимости проверять isFirstLineOfBlock логическое для каждой строки) ... Не знаю, как это сделать, кстати.

В любом случае, я был бы признателен за помощь в использовании потоков Java 8 для этого устройства чтения файлов.

Заключительный вопрос: в чем преимущество использования потоков Java для такого приложения: это просто вопрос читаемости или я могу ожидать улучшения производительности?

+0

Well Streams на самом деле не подходит для этого. Ваш код for-loop выглядит просто отлично. – Tunaki

+0

Это очень плохо ... Я таким образом закодировал путь Java 7 ... – Ben

+0

Тот же самый точный вопрос пришел ко мне.Похоже, что должен быть способ получить поток секционированных потоков. Таким образом, у вас могут быть разделы = lines.partitionBy (строка -> line.startsWith ("$")). Затем вы можете выполнить разделение .forEachOrdered() по очереди. – bigjosh

ответ

1

У вас есть несколько способов сделать это с помощью потоков Java 8. Например,

try (BufferedReader br = Files.newBufferedReader(Paths.get(filePath, Charset.defaultCharset())) { 
    br.lines() 
     .filter(line -> !line.startsWith("$")) 
     .forEachOrdered(currentParser::parseLine); 
} catch (IOException ex) { 
    throw new Error(ex); 
} 

.lines() Описание метод содержит

The Stream is lazily populated, i.e., 
read only occurs during the terminal stream operation. 

В этом примере работы терминала является forEachOrdered


еще один

try (Stream<String> stream = Files.lines(Paths.get(filePath, Charset.defaultCharset())) { 
    stream 
     .filter(line -> !line.startsWith("$")) 
     .forEachOrdered(currentParser::parseLine); 
} catch (IOException ex) { 
    throw new Error(ex); 
} 
1

Можно разобрать такие конструкции, с помощью моей свободной StreamEx библиотеки, которая увеличивает стандартный поток API:

StreamEx.ofLines(filePath, Charset.defaultCharset()) 
     .groupRuns((a, b) -> !b.startsWith("$")) 
     .forEachOrdered(list -> 
      list.subList(1, list.size()).forEach(getParser(list.get(0))::parseLine)); 

Здесь мы используем groupRuns метод, который сочетает в себе одну запись файла в списке. Аргументом, переданным в groupRuns, является BiPredicate, который применяется к паре смежных входных элементов, которые должны возвращать значение true, если элементы должны быть сгруппированы. Здесь мы группируем элементы, если только следующий начинается с "$". После этого мы лениво заселили Stream<List<String>> и проанализировали каждую группу, создав синтаксический анализатор, используя первую строку и вызывая parseLine для всех последующих строк.

Смежные вопросы