I could not come up with a solution that processes strings lazily. I am not sure if this is possible.
My solution is creating an ArrayList . If you need to use Stream , just name stream() on it.
public class DelimitedFile { public static void main(String[] args) throws IOException { List<String> lines = lines(Paths.get("delimited.txt"), "$$$$"); for (int i = 0; i < lines.size(); i++) { System.out.printf("%d:%n%s%n", i, lines.get(i)); } } public static List<String> lines(Path path, String delimiter) throws IOException { return Files.lines(path) .collect(ArrayList::new, new BiConsumer<ArrayList<String>, String>() { boolean add = true; @Override public void accept(ArrayList<String> lines, String line) { if (delimiter.equals(line)) { add = true; } else { if (add) { lines.add(line); add = false; } else { int i = lines.size() - 1; lines.set(i, lines.get(i) + '\n' + line); } } } }, ArrayList::addAll); } }
File contents:
bunch of lines with text
bunch of lines with text2
bunch of lines with text3
$$$$
2bunch of lines with text
2bunch of lines with text2
$$$$
3bunch of lines with text
3bunch of lines with text2
3bunch of lines with text3
3bunch of lines with text4
$$$$
Output:
0:
bunch of lines with text
bunch of lines with text2
bunch of lines with text3
1:
2bunch of lines with text
2bunch of lines with text2
2:
3bunch of lines with text
3bunch of lines with text2
3bunch of lines with text3
3bunch of lines with text4
Edit:
I finally came up with a solution that Stream lazily generates:
public static Stream<String> lines(Path path, String delimiter) throws IOException { Stream<String> lines = Files.lines(path); Iterator<String> iterator = lines.iterator(); return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>() { String nextLine; @Override public boolean hasNext() { if (nextLine != null) { return true; } while (iterator.hasNext()) { String line = iterator.next(); if (!delimiter.equals(line)) { nextLine = line; return true; } } lines.close(); return false; } @Override public String next() { if (!hasNext()) { throw new NoSuchElementException(); } StringBuilder sb = new StringBuilder(nextLine); nextLine = null; while (iterator.hasNext()) { String line = iterator.next(); if (delimiter.equals(line)) { break; } sb.append('\n').append(line); } return sb.toString(); } }, Spliterator.ORDERED | Spliterator.NONNULL | Spliterator.IMMUTABLE), false); }
This is actually / coincidentally very similar to the implementation of BufferedReader.lines() (which is used internally by Files.lines(Path) ). This may be less than the overhead to not use both of these methods, but use Files.newBufferedReader(Path) and BufferedReader.readLine() instead.
source share