简体   繁体   中英

in java how to efficiently read x lines from a file, close it, and then open it again, starting at line x and continue reading

BufferedReader has easy methods for reading a file line by line. But there doesn't seem to be anyway to keep track of where you are, so you can get back to that place later. FileInputStream has file getChannel() which returns a FileChannel which can tell you the current position in the stream. So if you give a BufferedReader a FileInputStream to read from, you can find out where the BufferedReader stopped reading in the FileInputStream, and you can also set the FileInputStream to that position before you give it to the buffered reader.

The problem is that the BufferedReader has read ahead in the file. So the position of the FileInputStream is not the same as the position in the BufferedReader. You may have read 20 lines from the BufferedReader, but the BufferedReader may have read 30 from the FileInputStream. If later you reopen the file, based on the position in the FileInputStream, you will have missed those intervening 10 lines.

I could reader character by character from the InputStream, but it seems like there is probably a better way...

This is an extremely difficult problem to solve using existing Java classes. For one reason, you've ignored the fact that you can't actually pass in an InputStream to a BufferedReader, you need to pass in a Reader.

Files deal in bytes, but Readers deal with Characters. Since any given character can take up an arbitrary number of bytes in an arbitrary character set, you would need to record how many bytes each character took up to be able to compute the number of bytes that a certain number of characters represent in the file.

If you are willing to go for a very fragile approach, you could assume that every byte in your file represents a character (eg ASCII) and that every line is terminated by "\\n" . Then it would just be a matter of recording how many characters you've read. Something like this:

public class CountingBufferedReader extends BufferedReader {
     private int position = 0;
     public String readLine() {
        String line = super.readLine();
        position += line.length() + 1;
        return line;
     }

     public int getPosition() {
         return position;
     }
}

Making it work generically for any input and any character set is much more difficult, and would probably involve rewriting many existing classes to be efficient.

It may not answer your question completely but Apache IOUtils will read the Stream into a List http://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#readLines%28java.io.InputStream%29

Then you can access any line in the list directly (the stream will have been closed)

from their docs:

public static List readLines(InputStream input)
                      throws IOException

    Get the contents of an InputStream as a list of Strings, one entry per line, using the default character encoding of the platform.

    This method buffers the input internally, so there is no need to use a BufferedInputStream.

    Parameters:
        input - the InputStream to read from, not null 
    Returns:
        the list of Strings, never null 
    Throws:
        NullPointerException - if the input is null 
        IOException - if an I/O error occurs
    Since:
        Commons IO 1.1

Use the java.io.LineNumberReader class. That tells you what line you're currently on. Next time just read that many lines. That does make your problem O(N**X) where X is the number of times you read the file, but then why are you only reading part of the file anyway?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM