`

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

相关推荐

    bytestreamdemo.zip

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

    android 上传文件

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

    JDK_API_1_6

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

    Java之IO流学习总结

    PushbackInputStream 的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。 StringBufferInputStream 已经被Deprecated,本身...

    Python源码-数学美之樱花.py

    Python源码-数学美之樱花

    蚁群算法(ACO)求解TSP问题,MATLAB源码,代码注释详细,可根据自身需求拓展应用

    蚁群算法(ACO)求解TSP问题,MATLAB源码,代码注释详细,可根据自身需求拓展应用

    2024年5月最新采集大众点评全国(内地)-学习培训大类-店铺基础信息,93余万家

    2024年5月最新采集大众点评全国(内地)-学习培训大类-店铺基础信息,93余万家。此处仅展示1万家,全量也有。 2024年5月最新大众点评店铺基础信息采集。含美食、休闲娱乐、结婚、电影演出赛事、丽人、酒店、亲子、周边游、运动健身、购物、家装、学习培训、医疗健康、爱车、宠物等十几大类共几千万家店铺信息。

    My-Graduation-Project-demo

    服务器

    C语言五子棋 人机战人人战Gobang.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    财务部部长月度绩效考核表.xlsx

    财务部部长月度绩效考核表.xlsx

    贪吃蛇.cpp

    贪吃蛇.cpp

    短路电流计算软件.zip

    短路电流计算软件

    蓝牙五子棋[可人机对战]Gobang.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    Python源码-小海龟之螺旋曲线.py

    Python源码-小海龟之螺旋曲线

    母亲节给妈妈的写个小祝福

    母亲节祝福python

    setuptools-58.0.4.tar.gz

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    setuptools-1.4.zip

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    setuptools-49.1.3.zip

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    单片机C语言Proteus仿真实例闪烁的LED

    单片机C语言Proteus仿真实例闪烁的LED提取方式是百度网盘分享地址

    五子棋imooc_wzq.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

Global site tag (gtag.js) - Google Analytics