PushbackInputStream 源码分析

扩展java.io.FilterInputStream,代表的是可放回输入流。用于根据特定字符来判断流类型或编码等。

[size=medium]1. 内部存储:[/size]


protected byte[] buf; // 缓冲区,从底层流获取数据

protected int pos; // 下一个要读取字节的位置

protected volatile InputStream in; // 底层流,继承自FilterInputStream


[size=medium]2. 构造函数:[/size]

public PushbackInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("size <= 0");
}
this.buf = new byte[size];
this.pos = size;
}

public PushbackInputStream(InputStream in) {
this(in, 1);
}


[size=medium]3. 读取[/size]

// 从缓冲区(+底层流)读取数据,放到指定数组上。返回读取字节的个数。
public int read(byte[] b, int off, int len) throws IOException {
ensureOpen();
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}

int avail = buf.length - pos; // 缓冲区可读取字节数
if (avail > 0) {
if (len < avail) { // 缓冲区可读取字节数够用
avail = len; // 给avail赋值,防止len-avail相减为负
}
System.arraycopy(buf, pos, b, off, avail); // 读取操作
pos += avail; // 读取位置后移 avail位
off += avail; // 偏移值后移 avail位
len -= avail; // 还需读取字节数
}
if (len > 0) { // 还需读取
len = super.read(b, off, len); // 从底层流读取
if (len == -1) { // 未读取到数据
return avail == 0 ? -1 : avail; // avail为0,表示都没有读取到数据;否则为从缓冲区读取数
}
return avail + len; // 总读取数 = 从缓冲区读取数 + 从底层流读取数
}
return avail; // 从缓冲区读取数
}


[size=medium]4. 放回[/size]

public void unread(int b) throws IOException {
ensureOpen();
if (pos == 0) { // 缓冲区已满
throw new IOException("Push back buffer is full");
}
buf[--pos] = (byte) b; // 然后读取位置回退,该位置重新设置为原来的值
}



public void unread(byte[] b, int off, int len) throws IOException {
ensureOpen();
if (len > pos) { // 缓冲区回退长度不够
throw new IOException("Push back buffer is full");
}
pos -= len; // 回退len个字节
System.arraycopy(b, off, buf, pos, len); // 将len个字节放回
}


[size=medium]5. 预估剩余可读取字节数[/size]

public int available() throws IOException {
ensureOpen();
return (buf.length - pos) + super.available(); // 缓冲区可读取数 + 底层可读取数
}


[size=medium]6. 跳过[/size]

public long skip(long n) throws IOException {
ensureOpen();
if (n <= 0) {
return 0;
}

long pskip = buf.length - pos; // 缓冲区可用存储位置个数
if (pskip > 0) {
if (n < pskip) { // 可用个数比跳过个数少
pskip = n; // pskip赋值为n,防止n -= pskip小于0
}
pos += pskip; // 读取位置后移pskip位
n -= pskip; // 还需跳过数
}
if (n > 0) { // 还需跳过n个字节
pskip += super.skip(n); // 总跳过字节数 = 缓冲区跳过数 + 底层流skip方法跳过数
}
return pskip;
}


[size=medium]7. 不支持mark相关的操作[/size]

public boolean markSupported() {
return false;
}

public synchronized void mark(int readlimit) {
}

public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}


[size=medium]8. 例子:去除UTF文件的BOM头[/size]

BOM (Byte Order Mark),包含三个字节"EF BB BF"。文件的开始位置有这几个字节,表明是UTF文件,读取数据的时候需要忽略它们。


public class BOMCleaner {

static final Logger LOG = LoggerFactory.getLogger(BOMCleaner.class);

static final int BOM_1 = 0xEF; // BOM第一个字节
static final int BOM_2 = 0xBB; // BOM第二个字节
static final int BOM_3 = 0xBF; // BOM第三个字节

public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("D:/mine.txt");
InputStream cleanIs = getInputStreamWithoutBom(fis);
}

public static InputStream getInputStreamWithoutBom(InputStream in) throws IOException {
PushbackInputStream pbIn = new PushbackInputStream(in, 3);
byte[] bytes = new byte[3];
int count = pbIn.read(bytes);
if (count == 3 && (bytes[0] & 0xFF) == BOM_1 && (bytes[1] & 0xFF) == BOM_2 && (bytes[2] & 0xFF) == BOM_3) {
LOG.debug("变更文件存在BOM头");
} else {
pbIn.unread(bytes);
}

return pbIn;
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`PushbackInputStream` 是Java IO库中的一个类,它提供了一种在读取输入流时将数据“推回”流中的方法,从而可以重新读取该数据。具体来说,`PushbackInputStream` 允许您在读取输入流时将最近读取的字节推回到流中,以便稍后再次读取它们。 例如,如果您正在读取一个文本文件,并且意外地读取了一些不是文本的字节,您可以使用 `PushbackInputStream` 将这些字节推回到流中,然后重新读取它们。 以下是一个示例代码片段,演示如何使用 `PushbackInputStream`: ```java import java.io.*; public class PushbackInputStreamExample { public static void main(String[] args) throws IOException { String str = "Hello, world!"; ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes()); PushbackInputStream pbis = new PushbackInputStream(bais); int b = pbis.read(); System.out.println((char) b); // Output: H pbis.unread(b); // push back the first byte read b = pbis.read(); // read the first byte again System.out.println((char) b); // Output: H byte[] buf = new byte[5]; pbis.read(buf); System.out.println(new String(buf)); // Output: "ello," pbis.unread(buf); // push back the five bytes read byte[] buf2 = new byte[5]; pbis.read(buf2); System.out.println(new String(buf2)); // Output: "ello," } } ``` 在这个示例中,我们首先将字符串 "Hello, world!" 转换为一个字节数组,然后使用 `ByteArrayInputStream` 将其包装为一个输入流。接下来,我们使用 `PushbackInputStream` 包装这个输入流。 我们首先读取第一个字节并将其输出。然后,我们使用 `unread` 方法将该字节推回流中,并再次读取它。然后,我们读取五个字节,并使用 `unread` 方法将它们推回流中。最后,我们再次读取五个字节,这次从流中读取了之前推回的字节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值