'readLine() reading from 1 line twice
I am trying to read the lines of a file. The first time I use br.readLine() it reads the first line. The second time it reads the second line etc. However, I like to start from the first line again for the second time. What do I need to do to tell it to start from the beginning again?
br.readLine(); //reading first line
br.readLine(); //I want it to read first line again
Solution 1:[1]
Assuming 'br' is a BufferedReader, you might try
assert br.markSupported();
br.mark(256); // assumes lines shorter than 256 characters
line1 = br.readLine();
br.reset();
line2 = br.readLine();
See the documentation.
Having said that, I'm unclear as to why it's necessary for you to read the line twice. Why can't you just read it once and use it twice?
Solution 2:[2]
The basic upshot is: That is impossible.
BufferedReader is fundamentally a streaming concept.
Imagine you have a file being streamed directly over a network that's 20GB in size. It is not feasible to keep a copy of all data sent so far in memory just in case you may at some point wish to start over from the beginning. It is also not feasible to save a copy to disk. That slows everything way down and wastes 20GB of disk spaces if you don't need to ever go back, and you usually don't.
Perhaps you say: Ah, but it's already a file. Okay, but BufferedReader is an abstraction that works just as well for data streamed over a network. This abstraction doesn't let you - because if it did, it could not be used for large files being streamed like that.
So, what's the fix? It depends on what's happening.
Fix 1: Just.. re-open
If whatever stream you are reading can be recreated at will (example: You can just.. close this bufferedreader and call Files.newBufferedReader a second time, no problem - if a file is where you are getting this from) - then do so.
Fix 2: Cache in full
Perhaps you know the data that's incoming is relatively small. Assuming it's also sent relatively quickly, one easy solution is to read it all in, and then you can repeatedly re-stream over the contents in memory. For example:
String data;
try (var in = someSocket.getInputStream()) {
data = new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
// now you can do this as many times as you want:
BufferedReader br = new BufferedReader(new StringReader(data));
Fix 3: Stream a copy to disk
You can also write code that saves everything you read to disk whilst processing it all. Assuming you go through the entire input and then you want to go through it again, open the file for the second runthrough.
Fix 4: Forget about BufferedReader
If you're just reading a file already, there's no need for BufferedReader which is an abstraction that fundamentally doesn't include at-will restarting. a FileChannel or RandomAccessFile object can be asked to reset their positions to 0. Note that they operate on bytes, not characters. This is particularly problematic if the data in it is a random-width character format such as the very common UTF-8.
Fix 5: limited rewind
BufferedReader does support a limited rewind feature, which is just implemented under the hood by saving the last X bytes read into memory. This is the 'mark' system. You have to decree how large that memory buffer can maximally be, and the BufferedReader object will immediately create a buffer of that size when you do so. Thus, when reading a 1GB file, you could open the reader, set a mark for 1GB worth of data, start reading, and now you can reset at will. But note that this will increase your JVM's memory footprint by 1GB immediately, and cannot be used to save any more than 2GB (because java does not let you make arrays larger than that).
Looks like this:
try (BufferedReader br = howeverYouGetIt()) {
// let's reread the stream 100 times!
for (int i = 0; i < 100; i++) {
if (i > 0) br.reset(); // don't reset the first time.
br.mark(1234567); // how large should it be?
// code that reads goes here
}
}
Fix 6: Reimagine the algorithm
Let's say that the only reason you need to go back to the start is for one relatively simple detail. Say, the file is a really really long list of first names and all you actually need to go through it a second time is to add up the amount of times that name 'Jane' shows up in the list. Then instead perhaps you want to just.. count those names, now there's no need to restart at all:
Map<String, Integer> nameCounts = new HashMap<>();
try (var br = howeverYouGetYourBufferedReader()) {
while (true) {
String name = br.readLine();
int count = nameCounts.getOrDefault(name, 0);
nameCounts.put(name, count + 1);
if (name.equals("Joe")) {
// let's say you want to go back and count all
// janes read so far when this happens...
int countOfJanes = nameCounts.get("Jane");
}
}
}
About 50 other options come to mind, but surely you get the idea.
Solution 3:[3]
You can use BufferedReader
First, put the file into BufferedReader
BufferedReader reader = new BufferedReader(new FileReader("src/main/resources/input.txt"));
Second, read all the lines
public String readAllLines(BufferedReader reader) throws IOException { StringBuilder content = new StringBuilder(); String line;
while ((line = reader.readLine()) != null) { content.append(line); content.append(System.lineSeparator()); } return content.toString();}
Good luck!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | passer-by |
| Solution 2 | rzwitserloot |
| Solution 3 | Joel |
