Java PushbackReader

时间:2020-01-09 10:36:06  来源:igfitidea点击:

当我们从Reader解析数据时,应使用Java PushbackReader类java.io.PushbackReader。有时,在确定如何解释当前字符之前,我们需要先阅读一些字符以查看即将出现的情况。 JavaPushbackReader允许我们执行此操作。好吧,实际上,它允许我们将读取的字符推回"阅读器"中。这些字符将在我们下次调用read()时再次读取。

JavaPushbackReader的工作方式与PushbackInputStream相似,不同之处在于PushbackReader适用于字符,而PushbackInputStream适用于字节。

PushbackReader示例

这是一个简单的" PushbackReader"示例:

PushbackReader pushbackReader =
    new PushbackReader(new FileReader("c:\data\input.txt"));

int data = pushbackReader.read();

pushbackReader.unread(data);

对read()的调用与从其他Reader读取的字符一样,从PushbackReader读取一个字符。对unread()的调用将一个字符推回PushbackReader中。下次调用read()时,将首先读取回退的字符。如果我们将多个字符推回" PushbackReader",则最新的推回字符将首先从" read()"方法返回,就像堆栈一样。

创建一个PushbackReader

要使用JavaPushbackReader,必须首先创建PushbackReader实例。我们可以使用" new"运算符,使用标准对象实例化来创建" PushbackReader"。必须将一个Java阅读器传递给PushbackReader构造函数,PushbackReader将从中读取其字符。这是创建JavaPushbackReader的示例:

PushbackReader pushbackReader =
    new PushbackReader(new FileReader("c:\data\input.txt"));

这个例子将Java FileReader传递给PushbackReader构造函数。这将使PushbackReaderFileReader中读取其字符。

设置PushbackReader的推回限制

我们可以在" PushbackReader"的构造函数中设置我们应该能够读取的字符数。这是使用PushbackReader构造函数设置推回限制的方法:

int pushbackLimit = 8;
PushbackReader reader = new PushbackReader(
                                new FileReader("c:\data\input.txt"),
                                pushbackLimit);

这个例子在PushbackReader中设置了一个8个字符的内部缓冲区。这意味着我们一次最多只能读8个字符,然后再读一次。

读取字符

我们可以从JavaPushbackReader中读取字符,就像从JavaReader中读取字符一样,因为PushbackReader是JavaReader子类。换句话说,我们可以使用从Reader类继承的read方法。这是一个通过JavaPushbackReaderread()方法读取字符的示例:

int aChar = pushbackReader.read();
while(aChar != -1) {
    System.out.println((char) aChar);
    aChar = pushbackReader.read();
}

如上例所示,read()返回一个int,我们必须自己将该int强制转换为char。当" PushbackReader"中没有可用字符时," read()"方法将返回" int"值-1.

推回字符

要将字符推回Java" PushbackReader",必须调用其" unread()"方法。这是将字符推回PushbackReader的示例:

int aChar = pushbackReader.read();

pushbackReader.unread(aChar);

aChar = pushbackReader.read(); // reads character pushed back

本示例从" PushbackReader"读取一个字符,然后将其推回" PushbackReader",然后再次从" PushbackReader"读取该字符。

关闭PushbackReader

当我们从PushbackReader中读取完字符后,我们应该记住将其关闭。关闭PushbackReader也将关闭PushbackReader正在读取的Reader实例。

关闭" PushbackReader"是通过调用其close()方法完成的。这是关闭PushbackReader的样子:

pushbackReader.close();

我们还可以使用Java 7中引入的try-with-resources构造。这是如何使用try-with-resources构造使用和关闭" PushbackReader"外观的方法:

Reader reader = new FileReader("data/data.bin");

try(PushbackReader pushbackReader =
    new PushbackReader(reader)){

    int data = pushbackReader.read();
    while(data != -1) {
        System.out.print((char) data);
        data = pushbackReader.read();
    }
}

请注意,不再有任何显式的close()方法调用。 try-with-resources构造可以解决这一问题。

还要注意,第一个FileReader实例不是在try-with-resources块内创建的。这意味着try-with-resources块不会自动关闭此FileReader实例。但是,当关闭" PushbackReader"时,也会关闭其读取的" Reader"实例,因此当" PushbackReader"关闭时," FileReader"实例也将关闭。

解析范例

在本JavaPusbackReader教程的最后,让我们看一个更详细的示例,说明在解析字符流时如何使用PushbackReader。本示例对将从" PushbackReader"读取的下一个"令牌"的第一个字符进行采样,以确定下一个令牌的类型,然后将字符推回以供相应的令牌生成器读取。在这种情况下,该示例有点"结构化",这意味着在实际的解析器中,我们可能会做一些不同的事情。但是,该示例主要用于说明在真实的解析示例中如何使用" PushbackReader",而不是作为编写出色的解析器的教科书示例。

public class TextTokenizer {
    protected PushbackReader pushbackReader =  null;

    public TextTokenizer(Reader reader) {
        this.pushbackReader = new PushbackReader(reader);
    }

    public String nextToken() {
        int firstChar = this.pushbackReader.read();
        this.pushbackReader.unread(firstChar);

        if(((char)firstChar) == '"') {
           return readDoubleQuotedToken();
        }
        if((char)firstChar) == '\'') {
           return readSingleQuotedToken();
        }
        return readSingleWordToken();
    }

    protected String readDoubleQuotedToken() { ... }

    protected String readSingleQuotedToken() { ... }

    protected String readSingleWordToken()   { ... }
}

这个例子有趣的部分是nextToken()方法。该方法首先将" PushbackReader"中的字符读入变量,然后将读取的字符推回" PushbackReader"中。这样,nextToken()方法就可以"采样"下一个令牌的第一个字符,并根据该值决定它是哪种令牌,以及调用哪种令牌的读取方法。

注意,对于单引号和双引号的标记,实际上没有必要将字符推回" PushbackReader",因为引号本身通常不包含在标记中。但是,对于readSingleTokenWord()来说,这是必需的,因为读取的字符是令牌值的第一个字符。

readDoubleQuotedToken(),readSingleQuotedToken()和readSingleWordToken()的实现已省略,以简化示例。想象一下,他们读取了用双引号("),单引号(')括起来的令牌或者以非单词字符结尾的令牌(例如空格,制表符,换行符等)。