`

PushbackInputStream 源码分析

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

1. 内部存储:

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

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

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


2. 构造函数:
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);
}


3. 读取
// 从缓冲区(+底层流)读取数据,放到指定数组上。返回读取字节的个数。
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; // 从缓冲区读取数
}


4. 放回
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个字节放回
}


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


6. 跳过
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;
}


7. 不支持mark相关的操作
public boolean markSupported() {
	return false;
}

public synchronized void mark(int readlimit) {
}

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


8. 例子:去除UTF文件的BOM头

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;
	}
}
分享到:
评论

相关推荐

    java中的回退流1

    在Java编程语言中,`PushbackInputStream`是一个用于处理输入流的类,它提供了一种回退(或称为退格)的功能。这个类是`java.io`包的一部分,主要用于在读取数据后允许将数据“推回”到流中,以便后续的读取操作可以...

    通过JDK源码学习InputStream详解

    - `PushbackInputStream`: 允许将已读取的字节“推回”到流中。 这些子类扩展了InputStream的功能,以满足不同场景的需求。 ### 应用场景 InputStream常用于读取各种数据源,如文件、网络连接、内存缓冲区等。通过...

    IO流所涉及到的Decorator设计模式

    **源码分析**: 在阅读Java IO源码时,可以看到这些Decorator类都遵循了相同的模式:定义一个与被装饰类相同接口的类,持有被装饰对象的实例,并在其方法中调用被装饰对象的方法,同时添加额外的功能。例如,...

    bytestreamdemo.zip

    java IO 字节流练习代码 FileInputStream和FileOutputStream BufferedInputStream 和 BufferedOutputStream DataInputStream 和 DataOutputStream ObjectInputStream和ObjectOutputStream ...PushbackInputStream

    java解压linux上的压缩文件gz格式文件

    ((PushbackInputStream) this.in).unread(buf, len - n, n); } else { byte[] b = new byte[1]; int ret = in.read(b); } } return charsRead; } } ``` 在这个类中,`MultiMemberGZIPInputStream` 扩展了 `...

    commons io 源代码

    - `PushbackInputStream` 和 `PushbackReader`:允许将数据推回输入流以便重读。 7. 文件观察和监听: - `FileAlterationObserver` 和 `FileAlterationListener`:监控文件系统的变化,例如文件创建、修改和删除...

    android 上传文件

    PushbackInputStream inStream = new PushbackInputStream(socket.getInputStream()); String response = StreamTool.readLine(inStream); System.out.println(response); String[] items = response...

    Java解决UTF-8的BOM问题

    在Java编程中,UTF-8编码是一个非常常见且广泛使用的字符编码格式,它能支持全球大部分语言的字符表示。然而,UTF-8有一个特殊特性,那就是它可以带有Byte Order Mark(BOM),这是一个特殊的字节序列,用于标识数据...

    彻底明白 Java 语言中的IO系统

    - **PushbackInputStream**: 允许将数据推回到输入流中,以便重新读取。 - **PushbackReader**: 字符流版本的`PushbackInputStream`。 这些FilterInputStream及其相应的输出流(如`DataOutputStream`、`...

    java输入输出流 流式输入与输出

    PushbackInputStream允许“回退”或退回已读取的字节,这在需要重新处理某些数据时非常有用。PipedStream允许两个线程间通过管道进行数据传输。 7. 文件复制示例 下面是一个使用Java I/O流进行文件复制的例子: ```...

    JDK_API_1_6

    PushbackInputStream PushbackInputStream 为另一个输入流添加性能,即“推回 (push back)”或“取消读取 (unread)”一个字节的能力。 PushbackReader 允许将字符推回到流的字符流 reader。 RandomAccessFile 此类...

    javaIO详细讲解+详细案例

    #### 六、案例分析 ##### 6.1 输入字节流案例 **案例1:读取文件内容** ```java FileInputStream in = new FileInputStream("test.txt"); byte[] b = new byte[1024]; int len; while ((len = in.read(b)) != -1)...

    文件字节IO流例子代码.rar

    实际的示例代码可能会包含这些概念的实际应用,也可能涵盖其他高级特性,如管道流(PipedInputStream和PipedOutputStream),或者装饰模式下的过滤流(如CheckedInputStream和PushbackInputStream)。要深入了解,你需要...

    IO流体系继承结构图_动力节点Java学院整理

    PushbackInputStream允许将已读取的数据重新送回输入流。此外,ObjectInputStream可以反序列化由ObjectOutputStream序列化的对象,SequenceInputStream则可以将多个InputStream连接起来,按顺序读取。 Reader和...

    IO流文档InputStream / OutputStream

    * PushbackInputStream OutputStream的类型: * ByteArrayOutputStream * FileOutputStream * PipedOutputStream * FilterOutputStream * ByteArrayOutputStream * DataOutputStream * BufferedOutputStream * ...

    JAVA IO流详解

    - `PushbackInputStream`允许在读取过程中将部分数据重新推回流中,主要用于高级解析场景。 #### 四、案例分析:增强FileOutputStream功能 假设我们需要实现一种功能,即首先将要写入文件的数据缓存在内存中,...

    Java IO口解析(初级)

    - **`PushbackInputStream`**:允许数据重新推回到流中。 #### 四、字符流详解 字符流主要用于文本数据的处理,更加高效且易于操作。 ##### 1. 基础字符流 - **`CharArrayReader`**:从一个字符数组创建输入流。...

    Java IO 流的操作

    - `PushbackInputStream` 允许读取的数据被“推回”到流中,以便再次读取。 6. Propertity类: - `Properties` 类用于处理属性列表(键值对),常见于配置文件的读写。 在实际编程中,我们通常会结合使用这些流...

    JavaIO流学习总结.pdf

    除了基本的输入输出流,还有一些特殊类型的流,如LineNumberInputStream提供行号功能,PushbackInputStream允许回退已读取的字节,而SequenceInputStream允许合并多个输入流。PrintStream是实用工具类,它可以格式化...

    Javaio流思维导图

    这就是FilterStream和PushbackInputStream等过滤流的作用,它们可以串联多个流,实现数据预处理、错误检测等功能。例如,DataInputStream和DataOutputStream可以对基本数据类型进行高效的读写。 管道流...

Global site tag (gtag.js) - Google Analytics