简体   繁体   中英

BufferedReader#readLine() hangs even though a line has been read

Updated Question (to be more clear):

Is there a way to design the InputStream below such that BufferedReader#readLine() will return after reading the new line character?

In the example below, readLine() hangs forever even though the reader has read a new line because (presumably) it is waiting for the buffer to fill up. Ideally, readLine() would return after reading the new line character.

I know something like what I want is possible, because when you read from System.in using BufferedReader#readLine() , it does not wait for the buffer to fill up before returning.

import java.io.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class Example {

    private static final class MyInputStream extends InputStream {

        public final BlockingQueue<String> lines = new LinkedBlockingQueue<>();
        private InputStream current = null;

        @Override
        public int read() throws IOException {
            try {
                if(current == null || current.available() == 0)
                    current = new ByteArrayInputStream(lines.take().getBytes("UTF-8"));
                return current.read();
            }
            catch(InterruptedException ex) {
                return -1;
            }
        }       
    }

    public static void main(String[] args) throws Exception {
        MyInputStream myin = new MyInputStream();
        myin.lines.offer("a line\n");
        BufferedReader in = new BufferedReader(new InputStreamReader(myin));
        System.out.println(in.readLine());
    }
}

Also, if there is a better way to send a string to an InputStream, I'm open to suggestions.

Accepted Solution:

Based on a suggestion from Sotirios Delimanolis in one of the comments on his solution, I'm just going to used a PipedInputStream instead. I've coupled it to a PipedOutputStream , and BufferedReader#readLine() returns immediately as long as I call PipedOutputStream#flush() after sending a string that contains a new line character.

After updated question, the only way to get the BufferedReader to stop reading after the new line character is to set the buffer size to 1 , which completely removes the need for a BufferedReader .

You'll have to write your own implementation.


A BufferedReader reads more bytes than required. In your case, that means it will read further than the new line character. For example, with the Oracle JVM, it will attempt to read 8192 bytes. Through your inheritance hierarchy, this

System.out.println(in.readLine());

will attempt to invoke your read() method 8192 times.

The first 6 calls will return a value, one for each of the characters in your String 's byte array. The next one, will see

if(current == null || current.available() == 0)
     current = new ByteArrayInputStream(lines.take().getBytes("UTF-8"));

and current.available() will return 0 since the ByteArrayInputStream has been fully consumed. It will then attempt to take from the BlockingQueue and block indefinitely.

Also, if there is a better way to send a string to an InputStream, I'm open to suggestions.

Well, instead of an InputStream you can try a BufferedReader , with something that looks like this:

public int read(String directory) throws Exception{
    String line = "";
    File file = new File(directory);
    FileReader fr = new FileReader(file);
    BufferedReader br = new BufferedReader(fr);

    do{
        lines.add(br.readLine());
    while(br.readLine() != null);

    br.close();

    return Integer.parseInt(line);
}

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