ZetCode

Java PushbackInputStream 类

最后修改时间:2025 年 4 月 16 日

java.io.PushbackInputStream 类为另一个输入流添加了推回 (pushback) 功能。 它允许将读取的字节推回流中。 这对于需要在流中“窥视”的解析场景非常有用。

PushbackInputStream 封装了另一个 InputStream,并为推回的字节提供了一个缓冲区。 默认的推回缓冲区大小为 1 字节,但可以指定自定义大小。 此类不是线程安全的,不能并发访问。

PushbackInputStream 类概述

PushbackInputStream 扩展了 FilterInputStream 并提供推回操作。 关键方法包括读取操作和 unread 方法,用于将字节推回。 推回缓冲区保存将要读取的字节。

public class PushbackInputStream extends FilterInputStream {
    public PushbackInputStream(InputStream in);
    public PushbackInputStream(InputStream in, int size);
    public int read();
    public int read(byte[] b, int off, int len);
    public void unread(int b);
    public void unread(byte[] b);
    public void unread(byte[] b, int off, int len);
    public int available();
    public long skip(long n);
    public boolean markSupported();
    public void close();
}

上面的代码显示了 PushbackInputStream 提供的关键方法。 这些方法允许读取数据并将字节推回流中。 该类不支持 mark/reset 操作。

创建 PushbackInputStream

通过将 PushbackInputStream 封装在另一个 InputStream 中来创建它。 您可以指定推回缓冲区大小或使用默认的 1 字节缓冲区。 缓冲区大小决定了可以推回的字节数。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

public class Main {

    public static void main(String[] args) {
        byte[] data = { 'A', 'B', 'C', 'D', 'E' };
        
        try {
            // Create with default buffer size (1 byte)
            ByteArrayInputStream bais1 = new ByteArrayInputStream(data);
            PushbackInputStream pbis1 = new PushbackInputStream(bais1);
            
            // Create with custom buffer size (5 bytes)
            ByteArrayInputStream bais2 = new ByteArrayInputStream(data);
            PushbackInputStream pbis2 = new PushbackInputStream(bais2, 5);
            
            System.out.println("Default buffer stream created");
            System.out.println("Custom buffer (5 bytes) stream created");
            
            pbis1.close();
            pbis2.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了创建 PushbackInputStream 的不同方法。 第一个使用默认缓冲区大小(1 字节),而第二个指定 5 个字节。 完成后始终关闭流以释放资源。 关闭 PushbackInputStream 时会自动关闭底层流。

读取和推回单个字节

最简单的用例是读取一个字节,并在需要时将其推回。 unread 方法将字节放回流的缓冲区中。 这允许稍后重新读取相同的字节。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

public class Main {

    public static void main(String[] args) {
        byte[] data = { 'A', 'B', 'C', 'D', 'E' };
        
        try (PushbackInputStream pbis = 
                new PushbackInputStream(new ByteArrayInputStream(data))) {
            
            // Read first byte
            int firstByte = pbis.read();
            System.out.println("First byte: " + (char) firstByte);
            
            // Push it back
            pbis.unread(firstByte);
            System.out.println("Byte pushed back");
            
            // Read again (same byte)
            int sameByte = pbis.read();
            System.out.println("Read again: " + (char) sameByte);
            
            // Continue reading
            int nextByte;
            while ((nextByte = pbis.read()) != -1) {
                System.out.println("Next byte: " + (char) nextByte);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了读取一个字节并将其推回流中。 下一次读取操作将返回推回的字节。 默认的 1 字节缓冲区足以满足此用例。 推回的字节数超过缓冲区大小会抛出 IOException。

推回多个字节

使用较大的缓冲区,可以推回多个字节。 unread 方法接受一个字节数组。 字节以相反的顺序推回(最后一个字节先推回)。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

public class Main {

    public static void main(String[] args) {
        byte[] data = { '1', '2', '3', '4', '5' };
        
        try (PushbackInputStream pbis = 
                new PushbackInputStream(new ByteArrayInputStream(data), 3)) {
            
            // Read first three bytes
            byte[] buffer = new byte[3];
            pbis.read(buffer);
            System.out.println("Read: " + new String(buffer));
            
            // Push them back
            pbis.unread(buffer);
            System.out.println("Pushed back 3 bytes");
            
            // Read again (same bytes)
            pbis.read(buffer);
            System.out.println("Read again: " + new String(buffer));
            
            // Read remaining bytes
            int remaining;
            while ((remaining = pbis.read()) != -1) {
                System.out.println("Remaining: " + (char) remaining);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了推回多个字节。 该流创建时具有 3 字节缓冲区。 读取 3 个字节后,它们被推回并再次读取。 请注意,推回的字节数超过缓冲区大小会抛出异常。

使用 PushbackInputStream 进行解析

PushbackInputStream 通常用于解析场景。 您可以提前读取以检查数据,然后如果字节与预期模式不匹配,则将字节推回。 这实现了灵活的解析,而不会消耗字节。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

public class Main {

    public static void main(String[] args) {
        String data = "123ABC456DEF";
        byte[] bytes = data.getBytes();
        
        try (PushbackInputStream pbis = 
                new PushbackInputStream(new ByteArrayInputStream(bytes), 10)) {
            
            int b;
            while ((b = pbis.read()) != -1) {
                if (Character.isDigit((char) b)) {
                    System.out.println("Number: " + (char) b);
                } else {
                    // Push back the non-digit
                    pbis.unread(b);
                    
                    // Read the alphabetic sequence
                    StringBuilder letters = new StringBuilder();
                    while ((b = pbis.read()) != -1 && 
                           Character.isLetter((char) b)) {
                        letters.append((char) b);
                    }
                    
                    // Push back the non-letter if not EOF
                    if (b != -1) pbis.unread(b);
                    
                    System.out.println("Letters: " + letters);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了解析混合的数字和字母数据。 当找到非数字时,它被推回,然后读取字母序列。 解析器根据窥视的字节在数字和字母处理之间切换。

处理推回缓冲区溢出

尝试推回超过缓冲区大小的字节数会抛出 IOException。 使用推回操作时,需要进行适当的错误处理。 在 unread 之前始终检查缓冲区容量。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

public class Main {

    public static void main(String[] args) {
        byte[] data = { 'X', 'Y', 'Z' };
        
        try (PushbackInputStream pbis = 
                new PushbackInputStream(new ByteArrayInputStream(data), 2)) {
            
            // Read two bytes
            byte[] buffer = new byte[2];
            pbis.read(buffer);
            System.out.println("Read: " + new String(buffer));
            
            // Push them back (okay)
            pbis.unread(buffer);
            System.out.println("Pushed back 2 bytes");
            
            try {
                // Try to push back one more byte (will overflow)
                pbis.unread('W');
                System.out.println("This won't be printed");
            } catch (IOException e) {
                System.out.println("Caught IOException: " + e.getMessage());
            }
            
            // Continue reading
            int remaining;
            while ((remaining = pbis.read()) != -1) {
                System.out.println("Remaining: " + (char) remaining);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了缓冲区溢出处理。 2 字节缓冲区允许推回两个字节。 尝试推回第三个字节会抛出 IOException。 适当的错误处理确保程序能够正常继续。

将 Pushback 与其他流结合使用

PushbackInputStream 可以与其他流类型结合使用,以进行复杂的 I/O 操作。 它通常与缓冲流一起使用以获得更好的性能。 推回功能的工作方式与底层流无关。

Main.java
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

public class Main {

    public static void main(String[] args) {
        byte[] data = new byte[100];
        for (int i = 0; i < data.length; i++) {
            data[i] = (byte) (i % 26 + 'A');
        }
        
        try (PushbackInputStream pbis = new PushbackInputStream(
                new BufferedInputStream(
                    new ByteArrayInputStream(data)), 10)) {
            
            // Read first 10 bytes
            byte[] buffer = new byte[10];
            pbis.read(buffer);
            System.out.println("First 10: " + new String(buffer));
            
            // Push them back
            pbis.unread(buffer);
            
            // Read again with different chunk size
            byte[] smallBuffer = new byte[3];
            pbis.read(smallBuffer);
            System.out.println("First 3 after pushback: " + 
                new String(smallBuffer));
            
            // Read remaining
            int bytesRead;
            while ((bytesRead = pbis.read(buffer)) != -1) {
                System.out.println("Read " + bytesRead + " bytes: " + 
                    new String(buffer, 0, bytesRead));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了 PushbackInputStream 封装了一个 BufferedInputStream。 这种组合提供了缓冲和推回功能。 推回操作的工作方式与底层缓冲流无关。

来源

Java PushbackInputStream 类文档

在本文中,我们介绍了 Java PushbackInputStream 类的基本方法和特性。 理解这些概念对于在 Java I/O 中进行解析和超前查看操作至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的敬业程序员。 我于 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和 8 本电子书。 凭借八年多的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程