IO流三:字节流文件读写操作、拷贝图片、带缓冲区的字节流、read()原理、读取键盘录入

1  字节流文件读写操作

字符流:FileReaderBufferedReader

    FileWriterBufferedWriter

字节流:InputStream :读

        OutputStream :写

字符流操作字符数据,多用于文本文件。字节流操作字节数据,例如图片文件等。

字符流读入字符数组char[],字节流读入字节数组byte[]

代码示例:

import java.io.*;

class FileStreamDemo {
	public static void main(String[] args) throws IOException{
		writeFile();
		readFile_1();
		readFile_2();
		readFile_3();
	}
	
	public static void writeFile() throws IOException{
		FileOutputStream fos = new FileOutputStream("fos.txt");//文件不存在则创建。
		
		//String类的getBytes()方法获取字节数组。
		fos.write("FileStream".getBytes()); 
		
	/* 	字节流的写入不需要刷新。字符流需要刷新是因为,先逐个字节的将数据从流对象写入到一个缓冲区,
	    再查表,有匹配的字符时,再刷新写入目的地。 */
		
		fos.close();
	}
	
	public static void readFile_1() throws IOException{
		FileInputStream fis = new FileInputStream("fos.txt");
		
		int ch = 0;
		
		while((ch=fis.read())!=-1){ //逐个字节读取
			System.out.println((char)ch);
		}
		fis.close();
	}
	
	public static void readFile_2() throws IOException{
		FileInputStream fis = new FileInputStream("fos.txt");
		
		byte[] buf = new byte[1024];
		int len = 0;
		
		while((len=fis.read(buf))!=-1){ //读取到buf数组中
			System.out.println(new String(buf,0,len));
		}
		fis.close();
	}
	
	public static void readFile_3() throws IOException {
		FileInputStream fis = new FileInputStream("fos.txt");
		
	  //int num = fis.available();
	  //available方法获取流对象中的字节数,回车符'\r'和换行符'\n'各占一个。
	  
	    byte[] buf = new byte[fis.available()]; //定义一个刚刚好的缓冲区,不用再循环了。
		                                    //但是如果这个数组长度过长,内存就溢出了。
		fis.read(buf);
		System.out.println(new String(buf));
		
		fis.close();
	}
}

2  拷贝图片

复制一个图片文件,用到的流对象:FileInputStreamFileOutputStream

 

思路:

1,字节流读取对象InputStream和图片文件相关联。

2,用字节流输出对象OutputStream创建一个文件,用于存储读取到底的图片数据。

3,通过循环读写,完成数据的存储。

代码示例:

import java.io.*;

class CopyPictureDemo{
	public static void main(String[] args){
		FileInputStream fis = null;
		FileOutputStream fos = null;
		
		try{
			fis = new FileInputStream("e:\\EVE.jpg"); //读
			fos = new FileOutputStream("e:\\EVE_副本.jpg"); //写
			
			byte[] buf = new byte[1024];
			int len = 0;
			
			while((len=fis.read(buf))!=-1){ //读取到数组中
				fos.write(buf,0,len);
			}
		}
		catch(IOException e){
			System.out.println(e.toString());
		}
		finally{
			try{
				if(fis!=null)
					fis.close();
			}
			catch(IOException e){
				System.out.println(e.toString());
			}
			try{
				if(fos!=null)
					fos.close();
			}
			catch(IOException e){
				System.out.println(e.toString());
			}
		}
	}
}

3  带缓冲区的字节流:复制MP3文件

通过复制MP3文件,演示带缓冲区的字节流:BufferedInputStreamBufferedOutputStream

代码示例:

import java.io.*;

class CopyMp3Demo {
	public static void main(String[] args) throws IOException{
		long start = System.currentTimeMillis(); //获取时间
		copy_1();
		long end = System.currentTimeMillis(); //获取时间
		
		System.out.println((end-start)+"毫秒"); //执行时间
	}
	
	//通过字节流的缓冲区完成复制
	public static void copy_1() throws IOException{
		BufferedInputStream bufis =
			new BufferedInputStream(new FileInputStream("e:\\GetUp.mp3"));
			
		BufferedOutputStream bufos = 
			new BufferedOutputStream(new FileOutputStream("e:\\GetUp-副本.mp3"));
		
		byte[] bt = new byte[1024];
		int len = 0;
		
		while((len=bufis.read(bt))!=-1){
			bufos.write(bt,0,len);
		}
		bufis.close();
		bufos.close();
	}
	
	//MyBufferedInputStream 的演示。
	public static void copy_2() throws IOException{
		MyBufferedInputStream bufis = 
			new MyBufferedInputStream(new FileInputStream("e:\\GetUp.mp3"));
		BufferedOutputStream bufos = 
			new BufferedOutputStream(new FileOutputStream("e:\\GetUp-副本.mp3"));
				
		int by = 0;
		
		while((by=bufis.myRead())!=-1){
			bufos.write(by);
		}
		bufis.myClose();
		bufos.close();
	}
}

4  自定义带缓冲区的字节流和read()方法的原理

上一节代码CopyMp3Democopy_2的演示。

代码示例:

import java.io.*;

class MyBufferedInputStream {
	private InputStream in = null;
	private byte[] buf = new byte[1024];
	
	//pos指针从数组中取元素,count计数器存储数组中的元素个数。
	private int pos = 0, count = 0;
	
	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;
			byte b = buf[pos];
			
			count--;
			pos++;
			return b&255; //从byte类型提升到int类型
		}
		else if(count>0){
			byte b = buf[pos];
			
			count--;
			pos++;
			return b&255; //从byte类型提升到int类型
		}
		return -1;
	}
	public void myClose() throws IOException{
		in.close();
	}
}

-1的二进制表示:补码表示

0000 0001

1111 1110

1111 1111   -1       读到81,就返回了-1,所以没有执行read方法。

 

为什么read()返回是int类型,而不是byte类型?

因为返回byte类型的话,遇到连续81时,返回-1,误以为是结束标记,就不执行whlie循环。

提升到int类型后,321才表示“-1”,才是结束标记。

byte: -1 ----> int: -1     //一个int类型是32个二进制位,而一个字节8个二进制位,所以被提升了。

 11111111 11111111 11111111 11111111 -1   //提升时若要还是-1,应在前面补1

---> 00000000 00000000 00000000 11111111    255  //但为了避免遇到81返回-1,被误以为是结束标记。

                                               //应该在前面补0

 

11111111   --->提升了一个int类型,那不还是-1吗?是-1的原因是因为在81前面补的是1导致的。

那么我只要在前面补0,既可以保留原字节数据不变,又可以避免-1的出现。

 

怎么补0呢?

  11111111 11111111 11111111 11111111

& 00000000 00000000 00000000 11111111     255    //255做与运算,补0.

--------------------------------------------

  00000000 00000000 00000000 11111111

 

 

事实上,read方法返回int类型时,进行补0的操作,

write方法先进行去0的操作,再把11111111写入目的地,最终写入的数据还是这一个字节11111111

 

5  读取键盘录入

System.out:对应标准的输出设备,控制台。

System.in:对应标准的输入设备,键盘。

 

需求:

1,通过键盘读取录入。

2,当录入一行数据后,就将该行数据进行打印。

3,如果录入“over”的话,那么停止录入。

其实就是带缓冲区的IO流中readLine()的原理。

 

代码示例:

import java.io.*;

class ReadInDemo{
	public static void main(String[] args) throws IOException{
		InputStream in = System.in; //System.in对象,读取键盘录入。
		StringBuilder sb = new StringBuilder();
		
		while(true){
			int ch = in.read();
			if(ch=='\r')
				continue;
			if(ch=='\n'){ //读到换行符,就打印缓冲区中的数据
				String s = sb.toString();
				if(s.equals("over"))
					break;
				System.out.println(s);
				sb.delete(0,sb.length()); //清空缓冲区。
			}
			else 
				sb.append((char)ch);
		}
	}
}


你可能感兴趣的:(Java基础)