我可以窥视 BufferedReader 吗?

22

有没有一种方法可以检查 BufferedReader 对象中是否有可读内容?类似于 C++ 中的 cin.peek()。谢谢。


2
我有一种感觉,C++的peek和Java的peek存在歧义。“检查是否有可读内容”与Java中的“peek”不匹配。 - BalusC
8个回答

40
你可以使用PushbackReader。使用它,你可以读取一个字符,然后将其取消读取。这实质上允许你将其推回去。
PushbackReader pr = new PushbackReader(reader);
char c = (char)pr.read();
// do something to look at c
pr.unread((int)c); //pushes the character back into the buffer

2
好的回答!这是Java中的“peek”。 - Pindatjuh
你的“Reader”可能会返回代码点。通过截断一个值然后将其推回缓冲区,你有可能破坏流。 - Zhro
请问数据是在流的末尾还是开头被推送?只是确认一下,因为我觉得创建类似于未读的东西,将字符推送到缓冲区的末尾会破坏数据,所以没有必要创建这样的东西。 - Umar Tahir

20

您可以尝试使用"boolean ready()"方法。 来自Java 6 API文档的描述:如果缓冲区不为空或底层字符流已准备好,则缓冲字符流是就绪的。

BufferedReader r = new BufferedReader(reader);
if(r.ready())
{
   r.read();
}

15
以下代码将查看流中的第一个字节。对于您来说,这应该像是一个预览。
BufferedReader bReader = new BufferedReader(inputStream);
bReader.mark(1);
int byte1 = bReader.read();
bReader.reset();

但是你不想要一个窥视,你想返回一个值? - Kyle Rosendo
是的,我确实想要一个 int peek()。如果流中有内容,通过检查返回值,我将得到答案,但流的状态不会改变。 - There is nothing we can do
1
@Thereisnothingwecando(我知道,已经7年了...但是对于其他人来说...)他的答案是一个真正的窥视等价物。你实际上可以创建一个扩展BufferedReader的类,并添加peek()方法,只需使用那3行代码。就这样。 - Daidon

4
正常的做法是在循环中检查BufferedReader#readLine()是否返回null。如果到达流的末尾(例如文件结尾,套接字关闭等),则它会返回null
例如:
BufferedReader reader = new BufferedReader(someReaderSource);
String line = null;
while ((line = reader.readLine()) != null) {
    // ...
}

如果你不想逐行读取(这也是选择BufferedReader的主要原因),那么可以使用BufferedReader#ready()代替:

BufferedReader reader = new BufferedReader(someReaderSource);
while (reader.ready()) {
    int data = reader.read();
    // ...
}

2
BufferedReader br = new BufferedReader(reader);
br.mark(1);
int firstByte = br.read();
br.reset();

1
我的解决方案是...扩展BufferedReader并使用队列作为缓冲区,然后您可以在队列中使用peek方法。
public class PeekBufferedReader extends BufferedReader{

    private Queue<String>       buf;
    private int                 bufSize;

    public PeekBufferedReader(Reader reader, int bufSize) throws IOException {
        super(reader);
        this.bufSize = bufSize;
        buf = Queues.newArrayBlockingQueue(bufSize);
    }

    /**
     * readAheadLimit is set to 1048576. Line which has length over readAheadLimit 
     * will cause IOException.
     * @throws IOException 
     **/
    //public String peekLine() throws IOException {
    //  super.mark(1048576);
    //  String peekedLine = super.readLine();
    //  super.reset();
    //  return peekedLine;
    //}

    /**
     * This method can be implemented by mark and reset methods. But performance of 
     * this implementation is better ( about 2times) than using mark and reset  
     **/
    public String peekLine() throws IOException {
        if (buf.isEmpty()) {
            while (buf.size() < bufSize) {
                String readLine = super.readLine();
                if (readLine == null) {
                    break;
                } else {
                    buf.add(readLine);
                }
            }
        } else {
            return buf.peek();
        }
        if (buf.isEmpty()) {
            return null;
        } else {
            return buf.peek();
        }
    }

    public String readLine() throws IOException {
        if (buf.isEmpty()) {
            while (buf.size() < bufSize) {
                String readLine = super.readLine();
                if (readLine == null) {
                    break;
                } else {
                    buf.add(readLine);
                }
            }
        } else {
            return buf.poll();
        }
        if (buf.isEmpty()) {
            return null;
        } else {
            return buf.poll();
        }
    }
    public boolean isEmpty() throws IOException {
        if (buf.isEmpty()) {
            while (buf.size() < bufSize) {
                String readLine = super.readLine();
                if (readLine == null) {
                    break;
                } else {
                    buf.add(readLine);
                }
            }
        } else {
            return false;
        }
        if (buf.isEmpty()) {
            return true;
        } else {
            return false;
        }
    }
}

1
你的解决方案背后的想法相当聪明,但我认为我们不需要扩展BufferStreamReader,而是使用队列,在窥视并获取数据包大小或执行与协议相关的处理时将字节放入其中。这将有点像拥有自己的缓冲区 :) - Umar Tahir

1
你可以使用PushBackReader来读取一个字符,然后“推回”它。这样你就可以确定有东西存在,而不会影响其整体状态——一种“窥视”。

推回读取器是如何工作的?我猜它会在缓冲区读取的开头推送数据,从而维护消息的整体状态,从而避免数据包损坏。 - Umar Tahir

1

pgmura的答案(依赖于ready()方法)简单且有效。但请记住,这是因为Sun公司对该方法的实现;它并不完全符合文档。如果此行为很关键,我不会依赖它。 在这里查看http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4090471 我更倾向于选择PushbackReader选项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接