InputStream的重复读取
问题的引入
流只能读取一次,用System.out.println();打印了流的话 就会导致流被读取了一次 之后再读取的话 就会导致流的使用受到影响
解决方式
①使用缓存
我们可以使用ByteArrayOutputStream将流数据缓存到内存中,达到多次读取的目的。
/*
利用ByteArrayOutputStream缓存InputStream,以便InputStream的重复使用
具体实现:先读取inputStream中的数据 然后写入 ByteArrayOutputStream中 下次可以重复从ByteArrayOutputStream中读取
缺点:要缓存这个InputStream内存压力可能是比较大的。如果第一次读取只是需要判断该文件流的类型,文件编码等用的话,往往不需要所有的InputStream的数据,或者说只需要前n个字节的话,
这样一来缓存整个InputStream实际上是一种浪费
*/
public class IoAgain {
private ByteArrayOutputStream byteArrayOutputStream=null;
public IoAgain(InputStream inputStream) throws IOException {
if(inputStream==null){
return;
}
byteArrayOutputStream=new ByteArrayOutputStream();
byte[] buffer=new byte[1024];
int len;
//先将流读取出来 然后再写入到ByteArrayOutputStream中
while ((len=inputStream.read(buffer))>-1){
byteArrayOutputStream.write(buffer,0,len);
}
byteArrayOutputStream.flush();
}
public ByteArrayInputStream getInputeStream() throws IOException {
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
}
public static void main(String[] args) throws IOException {
InputStream is=new BufferedInputStream(new FileInputStream("d://a.txt"));
IoAgain again=new IoAgain(is);
ByteArrayInputStream byteArrayOutputStream=again.getInputeStream();
InputStreamReader isr=new InputStreamReader(byteArrayOutputStream);
BufferedReader br=new BufferedReader(isr);
String str=null;
while ((str=br.readLine())!=null){
System.out.print(str+"\n");
}
}
}
②通过mark和reset()方法重复利用InputStream()
InputStream抽象类本身提供了三个抽象的方法
①InputStream是否支持mark(),默认为不支持
public boolean markSupported() { return false; }
②mark(),该接口在InputStream中默认实现不做任何事情
public synchronized void mark(int readlimit) {}
③reset接口,该接口在InputStream中实现
public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); }
子类需要重写这三个方法,整个的实现过程是:调用mark()方法会记录下当前调用mark方法的时刻,InputStream被读到的位置,调用reset()方法就会回到该位置
举例实现如下:(由于)
/*
通过标记实现InputStream的可重复读
ByteArrayInputStream可以进行无数次的标记
优点:不用缓存整个InputStream数据。对于ByteArrayInputStream甚至没有任何的内存开销
缺点:就是需要通过干扰InputStream的读取细节,也相对比较复杂
*/
public class IOAagin {
public static void main(String[] args) throws IOException {
String content="zsdgefa!df";
InputStream inputStream=new ByteArrayInputStream(content.getBytes());
//判断该字符流是否支持marked操作
if(!inputStream.markSupported()){
System.out.println("该流不支持标记");
}
int ch;
boolean marked=false;
byte[] b=new byte[2];
//读取字符
while ((ch= inputStream.read())!=-1){
//单个读取字符
System.out.print((char) ch);
//读到e的时候标记一下
if((char)ch=='e'&&!marked){
//mark方法的参数表明了在读取了这么多个字符之前mark是有效的 读过后mark标记是无效的 不同的子类实现的结果不一样
//BufferArrayInputStream可以标记无数次
inputStream.mark(content.length);
marked=true;
}
//当读到!的时候就从标记位置开始读
if((char)ch=='!'&&marked){
inputStream.reset();//调用reset()方法就会回到inputStream中被标记的位置
marked=false;
}
}
}
}
备注:
①常用的FileInputStream不支持mark
②对于BufferedInputStream,readlimit表示:InputStream调用mark方法的时刻起,在读取readlimit个字节之前,标记的该位置是有效的。如果读取的字节数大于readlimit,可能标记的位置会失效。
③对于InputStream的另外一个实现类:ByteArrayInputStream,我们发现readlimit参数根本就没有用,调用mark方法的时候写多少都无所谓。