黑马程序员-IO流的小总结(上)

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

IO流分为两种:
字节流:传输多媒体文件用字节流,单位是字节。字节流是通用的。
字符流:传输文字字符文件用字符流,里面对应的是各种字符码表来进行翻译。字符流基于字节流。
字节流抽象基类:
InputStream:读
OutputStream:写
字符流抽象基类:
Reader:读
Writer:写

 FileWriter:
创建一个FileWriter对象。该对象一被初始化就必须明确被操作的文件。而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。如果需要对已有文件的末尾处进行续写,要用FileWriter fw=new FileWriter("文件",true);这里true表示,进行续写,不覆盖。内容中\n表示换行。但是txt文档中需要\n\r才能换行。
FileWriter fw=new FileWriter("demo.txt");//其实该步就是明确数据要存放的目的地。
fw.write("abcde");//write()方法把字符串写入流中,还没有写进入文件中。
fw.flush();//刷新流对象的缓冲数据,将数据刷到目的文件中
fw.close();//关闭这个流资源。再用需要重新new一个。但是这次关闭,会自动把流中还没有来得及刷新出去的数据给刷新到目的文件中。

 FieldReader
FileReader fr=new FileReader("demo.txt");
第一种方式:单个读取字符
//调用读取流对象的read方法。
int ch=fr.read();//每次读取一个字符。而且会自动向下读,读到文件末尾返回-1。返回是int类型的ASCII值.可以利用这个-1作为条件用循环读取文件。
第二种方式:通过字符数组进行读取。
char[]buf=new c har[3];
int num=fr.read(buf);
这里read方法将字符读入buf数组。如果数组长度不够,就能装入几个装几个。当再次执行read时,若是同一数组,则继续向后读字符串,将数组从头覆盖。如果流中字符串读完,将不再每次read,返回int类型值为数组中新进入元素的个数。如果存入数组,返回-1.可以以此-1作为循环条件。buf数组大小一般在定义的时候会定义为1024的整数倍,一个字符占两个字节,1024个字符大小就是2k。

用FileWriter FileReader进行文件的复制。‘两种方式,一种单个字符写,一种用数组。

public class CopyDemo {
	public static void main(String[]args) throws IOException{
		copy_2();
	}
	
	public static void copy_1() throws IOException{
		//创建目的地
		FileWriter fw=new FileWriter("E:\\Demo_copy.txt");
		
		//与已有文件关联
		FileReader fr=new FileReader("demo.txt");
		
		int ch=0;
		while((ch=fr.read())!=-1){
			fw.write((char)ch);
		}
		fw.close();
		fr.close();
	}
	
	public static void copy_2(){
		FileWriter fw=null;
		FileReader fr=null;
		try{
			fw=new FileWriter("E:\\Demo_copy.txt");
			fr=new FileReader("demo.txt");
			char[] ch=new char[1024];
			int num=0;
			while((num=fr.read(ch))!=-1){
				fw.write(ch, 0, num);
			}
		}
		catch(IOException e){
			throw new RuntimeException("读写失败");
		}
		finally{
			if(fr!=null){
			try {
				fr.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			}
			if(fw!=null){
				try {
					fw.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}


缓冲区的出现是为了提高流操作效率而出现的。创建缓冲区之前,必须先有流对象BufferedWriter。

BufferedWriter bufw=new BufferedWriter(fw);//将流对象作为参数传递给缓冲区的构造函数即可。
记住:只要用到缓冲区就要记得刷新,bufw.flush()
缓冲区中提供了一个跨平台的换行符,bufw.newLine();效果等同于windows系统下的输入\r\n,linux下的\r。
关闭缓冲区,bufw.close() 直接关闭了缓冲区和流对象,所以没有必要再关闭流对象了。
BufferedReader bufr=new BufferedReader(fr);//创建读取流缓冲区
String line=null;
while((line=bufr.readLine())!=null)//BufferedReader的特有方法readLine()返回是String类型的流文件中的一行读到流文件结尾处,返回null
{
...
}
bufr.close();

利用缓冲区进行文件的复制

public class BufferCopyDemo {
	public static void main(String[]args){
		BufferedReader bufr=null;
		BufferedWriter bufw=null;
		
		try {
			bufr=new BufferedReader(new FileReader("D:\\workspace\\exam\\src\\com\\itheima\\BufferCopyDemo.java"));
			bufw=new BufferedWriter(new FileWriter("E:\\copy_BufferedCopyDemo.txt"));
			String s=null;
			while((s=bufr.readLine())!=null){
				bufw.write(s);
				bufw.flush();//记得写一次就刷新一次,免得出现停电这种情况,缓冲区中的数据就都没了
				bufw.newLine();
			}
		} catch (IOException e) {
			throw new RuntimeException("读写失败");
		}finally{
			if(bufr!=null){
				try {
					bufr.close();
				} catch (IOException e) {
					throw new RuntimeException("读取失败");
				}
				try {
					bufw.close();
				} catch (IOException e) {
					throw new RuntimeException("写入失败");
				}
			}
		}
	}
}

字节流:
字节流中write方法中字符串需要用.getBytes()方法处理一下,因为字节流中操作的参数必须都是Byte类型的。
字节流write后,不需要刷新缓冲区。原因:其实字符流的底层也是字节流,但是字符流中处理每个字符都需要读取2个字节,所以需要在流中做缓冲处理,再都刷新出来。而字节流是来一个字节就写出去,不需要进行缓存处理了。
FileInputStream fis=new FileInputStream("...");//一个读流
byte[]buf=new byte[fis.available()];//定义一个刚好的缓冲区,不用再数组循环了。available()方法是获得文件
fis.read(buf); //字节个数,一个回车是2个字节\r\n。此方法慎用,因为文件太大将内存溢出
System.out.println(new String(buf));
fis.close;


关于复制MP3文件中read方法中的与运算原因。

/*拷贝一个mp3文件
 * */
public class Copymp3 {
	static FileInputStream fis=null;
	static FileOutputStream fos=null;
	static BufferedInputStream bis=null;
	static BufferedOutputStream bos = null;
	
	public static void main(String[]args){
		long start=System.currentTimeMillis();
		copy1();
		long end=System.currentTimeMillis();
		System.out.println((end-start)+"毫秒");
		
		long start1=System.currentTimeMillis();
		copy2();
		long end1=System.currentTimeMillis();
		System.out.println((end1-start1)+"毫秒");
	}
	//通过字节流缓冲区完成复制
	public static void copy1(){
		try {
			
			fis=new FileInputStream("e:\\Again.mp3");
			fos=new FileOutputStream("e:\\Again2.mp3");
			
			bis=new BufferedInputStream(fis);
			bos=new BufferedOutputStream(fos);
			
			int by=0;
			
			while((by=bis.read())!=-1){
				bos.write(by);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			
				try {
					if(bos!=null)
					bos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					throw new RuntimeException();
				}
				
				try {
					if(bis!=null)
					bis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					throw new RuntimeException();
				}
			}
		}
	
	public static void copy2(){//通过自制的字节流缓冲区完成复制
		MyBufferedInputStream mybis=null;
		BufferedOutputStream mybos=null;
		try {
			fis=new FileInputStream("e:\\Again.mp3");
			fos=new FileOutputStream("e:\\Again3.mp3");
			
			mybis=new MyBufferedInputStream(fis);
			bos=new BufferedOutputStream(fos);
			
			int len=0;
			while((len=mybis.myRead())!=-1){
				bos.write(len);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally{
			try {
				if(bos!=null)
				bos.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				throw new RuntimeException();
			}
			
			try {
				if(mybis!=null)
				mybis.myClose();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				throw new RuntimeException();
			}
		}
	}
}
public class MyBufferedInputStream {//自定义的缓冲字节流
	private InputStream in;
	private byte[] buf=new byte[1024*4];
	private int pos=0,count=0;//count表示buf数组中还没有被读到的已存的字节数
	MyBufferedInputStream(InputStream in){
		this.in=in;
	}
	
	//一次读一个字节,从缓冲区(字节数组中获取)
	public int myRead() throws IOException{
		//通过in对象读取硬盘上的数据,并存储到buf中。
		if(count==0){
		count=in.read(buf); 
		if(count<0) return -1;//流中没有数据了
		pos=0;//每次从硬盘向数组缓存数据数组的指针都要归0
		byte b=buf[pos];
		count--;
		pos++;
		return b&255;//b与11111111做与运算,转变为32位的int类型,其中前面24位是0。
		}
		else if(count>0){
			byte b=buf[pos];
			count--;
			pos++;
			return b&0xff;//b与11111111做与运算,转变为32位的int类型,其中前面24位是0
		}else return -1;
	}
	
	public void myClose() throws IOException{
		in.close();
	}
}
mp3文件本身就是一个字节流的01代码文件,其开头处很可能是连续8个1,十进制值为-1。read方法读取的是char类型值,返回的却是int类型的值。当read方法中一遇到这8个1,就收录,再类型转换为int型,即前面都补1到32位。这时这32个连续的1十进制值还是-1,read方法返回这个-1,程序判断是文件流扫描到了末尾,就不再循环,流关闭。那么在处理这个问题时,我们,必须想办法把32个1前面的补的这24个1都变为0,那么就要把这32个1和255做与运算。与后出来的int类型十进制为255而不再是-1.

同时可以推断出来,write方法中接收4字节的int类型,执行时把int类型数据中前面的24个bit都砍掉,只留后面的8个bit。等于是将数据又从int类型转换为了byte。详情见Copymp3.java,MyBufferedInputStream.java。


读取键盘录入:

System.out:对应的是标准的输出设备,控制台。
System.in:对应的标准输入设备,键盘。字节读取流对象。

 转换流:字节流对象转化为字符流

/*通过键盘录入数据,当录入一行数据后,就将该行数据进行打印,如果录入数据是over,那么停止录入*/
public class TransStreamDemo {
	static InputStream in=System.in;//声明一个键盘录入对象	
	static OutputStream out=System.out;//声明一个屏幕输出对象,不用输出语句
	public static void main(String[]args) throws IOException{
		System.out.println("start:");
		/*int b=in.read();
		int b1=in.read();
		int b2=in.read();
		int b3=in.read();
		
		System.out.print(b);
		System.out.print(b1);
		System.out.print(b2);
		System.out.print(b3);*/
		
		/*
		int ch=0;
		while((ch=in.read())!=-1){
			System.out.println(ch);
		}*/
		
			//A();
		//B();
		C();
	}
	//下面其实也就是字符流BufferedReader中readLine()方法的原理。
	public static void A() throws IOException{
		StringBuilder sb=new StringBuilder();
		while(true){
			int ch=in.read();

			if(ch=='\r')
				continue;
			if(ch=='\n'){
				String s=sb.toString();
				
				if("over".equals(s))break;
				
				System.out.println(s.toUpperCase());
				
				sb.delete(0, sb.length());
			}else
			sb.append((char)ch);
		}
	}
	
	
	//readLine方法是字符流BufferdeReader类中的方法,而键盘录入的read方法是字节流InputStream的方法。
	//需要把字节流转换为字符流,就可以调用readLine方法了
	public static void B() throws IOException{
		//将字节流对象转成字符流对象,使用转换流。InputStreamReader,它本身是字符流
		InputStreamReader isr=new InputStreamReader(in);
		
		//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
		BufferedReader bufr=new BufferedReader(isr);
		
		String s=null;
		
		while((s=bufr.readLine())!=null){
			if(s.equals("over"))break;
			System.out.println(s.toUpperCase());
		}
		
		bufr.close();
	}
	
	//字符流转换为字节流
	public static void C() throws IOException{
		//将字节流对象转成字符流对象,使用转换流InputStreamReader,它本身是字符流
		InputStreamReader isr=new InputStreamReader(in);
				
		//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
		BufferedReader bufr=new BufferedReader(isr);
		
		//以上两句可以用一句表达。太残暴了。这句一定要几下来,键盘录入的标准形式!!!
		//键盘录入字节流对象->键盘录入字符流对象->键盘录入字符流缓冲区对象
		//BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
		
		//将字节输出流对象转换为字符输出流对象,记得字符输出流需要flush()
		OutputStreamWriter osw=new OutputStreamWriter(out);
		
		//将字符输出流对象包装为字符输出流缓冲区
		BufferedWriter bufw=new BufferedWriter(osw);
		
		//以上两句可以用一句表达.太残暴
		//屏幕输出字符流缓冲区对象->屏幕输出字符流对象->屏幕输出字节流对象
		//BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
		//同样的,输出目的地不是屏幕而是一个文件的话
		//BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("文件")));
		
		String s=null;
		while((s=bufr.readLine())!=null){
			if("over".equals(s))break;
			bufw.write(s.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
		bufw.close();
	}
}
上面例子显示了转换流的高效和方便。

流操作的基本规律:
最痛苦的就是流对象太多,不知道用哪一个。
通过三个明确来完成。
1 明确源和目的。
源:输入流。InputStream Reader
目的:输出流 OutputStream Writer


2 操作的数据是否是纯文本。
是:字符流。
不是:字节流。


3 当体系明确后,再明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台

如:将一个文本文件中数据存储到另一个文件中,即复制文件。
源:因为是源 所以使用读取流InputStream Reader
是不是操作文本文件。
是:选择Reader
这样体系就明确了。
接下来明确要使用该体系中的哪个对象。
明确设备:硬盘,即一个文件。
Reader体系中可以操作文件的对象是FileReader

是否需要提高效率:是,加入Reader体系中的缓冲区BufferedReader
FileReader fr=new FileReader("a.txt");
BufferedReader bufr=new BufferedReader(fr);


目的:OutputStream Writer。
是否纯文本
是:Writer
设备:硬盘,即一个文件。
Writer体系中可以操作文件的对象FileWriter

FileWriter fw=new FileWriter("b.txt"); 
BufferedWriter bufw=new BufferedWriter(fw);


 扩展:想要把录入的数据按照指定的编码表(utf-8),将数据存入文件。
目的:OutputStream Writer
是否纯文本 是 Writer。因为牵扯到各种编码、中文,不能用简单的字节流了。
设备:硬盘。一个文件,使用FileWriter。
但是FileWriter是使用的默认编码GBK。
需要指定utf-8的话,只能用转换流,因为它有这个指定编码表的方法。
确定使用OutputStreamWriter.
而该转换流对象要接受一个字节输出流,且这个流可以操作文件,那么就是FileOutputStream
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("文件"),"UTF-8");
需要高效吗?需要
BufferedWriter bufw=new BufferedWriter(osw);

所以,转换流什么时候使用。
字符和字节之间的桥梁,一般,涉及到字符编码转换时需要用到转换流。

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值