Java核心语法——IO流

IO流

概述

简述

I\O:input、output。输入输出都是针对内存而言
input:输入,其他设备到内存
output:输出,内存到其他设备

什么是I\O流?

1)I\O就是设备之间进行数据交互的对象所属的类型
2)Java中操作设备之间数据传输的对象,都是IO流对象,这些对象所属的类型,都在io包中

分类

按照共能分为:

字节流:直接操作字节的流对象
字符流:直接操作字符的流对象

按照流向分为:

输入流:其他设备流到内存
输出流:内存流到其他设备

所以有四个顶层抽象类

字节流:
	字节输入流:InputStream
	字节输出流:OutputStream
字符流:
	字符输入流:Reader
	字符输出流:Writer

PS:根据不同的交互设备,有不同的具体子类,但是没必要那么详细,直接使用对应的顶层抽象类即可(向上转型)

步骤

1)导包导入io包
2)处理解决异常
3)关闭io流对象

字节流

计算机最小存储单位是字节,那么字节可以表示任意类型

字节流:
字节输入流:InputStream
字节输出流:OutputStream

InputStream

字节输入流的顶层抽象父类。因为其中的read()方法是抽象方法(不知道具体的交互设备),那么对应的类就是抽象类。

注意: InputStream是一个抽象类,那么创建对象时,使用用子类创建

方法

int read()从当前字节输入流中,读取并返回一个字节,未读到返回-1
注意
	既然是字节,为什么是int类型?
	因为字节不能表示负数,无法指名没有读到字节的情况,所以,将字节前面加24个0,变成int

int read(byte[] arr)从当前字节输入流中,读取并存放在arr中,未读到返回-1。
注意
	连续使用时,会接着上次的内容继续读取并存放
	
int available()剩余未读的字节个数
void close()4
FileInputStream
概述

InputStream具体的子类,最常用的,与磁盘交互

不仅一次可以读取一个字节,一次也可以读取很多字节
因为一切数据在计算机中都是以字节存储,所以FileInputStream可以读取任意文件,包括图片、视频、音频

构造方法

FileInputStream(File f)将file对象封装成字节输入流,将来可以读取文件的信息
FileInputStream(String path)将字符串(文件路径)封装成字节输入流,将来可以读取信息

注意:封装的文件对象都是针对文件本身而言,不是封装路径

	FileInputStream f = new FileInputStream("a.txt");
	byte[] arr = new byte[2];
	int length;
	while((length = f.read(arr)) != -1) {
		//第一次读取两个字节,并存放在arr中,arr{1,2}
		//第二次读取一个字节,并存放在arr中,arr{3,2},覆盖掉原本的0索引上的元素
		//第三次没有读取到字节,返回-1,并结束循环
		System.out.print(new String(arr,0,length) + "\t");
		System.out.println(f.available());
	}
	f.close();
	FileInputStream fis = new FileInputStream("D:\\www\\baidu\\test.txt");
	int a;
	while((a = fis.read()) != -1) {
		System.out.println(a);
	}

OutputStream

字节输出流的顶层抽象父类

注意: OutputStream是一个抽象类(不知道关联的具体的设备是什么),那么创建对象时,使用用子类创建

方法

void write(int b)
注意
	因为是字节流,而存储的又是int类型,所以存储时会将前三个字节(全是0)取消,只存后一个字节的内容
void write(byte[] arr)
void write(byte[] arr,int offest,int len)
	offest:索引开始的位置
	len:总共输出多少位
void close()
FileOutputStream

OutputStream具体的子类,最常用的,与磁盘交互

构造方法
FileOutputStream(String path)
FileOutputStream(File f)
 	1)指定的路径不存在的,创建该文件
 	2)指定的路径是存在的,清空该文件

FileOutputStream(String path, boolean append)
FileOutputStream(File f, boolean append)
 	1)指定的路径不存在,自动创建
 	2)指定的路径存在,且append设置为true,则不会清空该文件,而是在后面追加
	FileOutputStream fos = new FileOutputStream("test.txt", true); //原内容为123
	byte[] arr = {65, 66, 67};
	fos.write(arr, 0, 2);//123AB
	fos.close();

字节流文件拷贝

普通的

在这里插入图片描述

一、低效率,使用内存少
一个字节一个字节的拷贝

FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("a_copy.txt");
int a = 0;//用于判断是否还有没读完的字节
while((a = fis.read()) != -1) {
	fos.write(a);
}

fis.close();
fos.close();

二、高效率,连续占用的内存大,不贴近实际
使用一个数组一次拷贝

FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("a_copy_arr.txt");
byte[] arr = new byte[fis.available()];
fis.read(arr);
fos.write(arr);
		
fis.close();
fos.close();

三、高效率,连续占用的内存较小,贴近实际
使用一个数组多次拷贝

FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("a_copy_arrs.txt");
byte[] arr = new byte[1024 * 8];
int len = 0;
while((len = fis.read(arr)) != -1) {
	fos.write(arr, 0, len);
}
fis.close();
fos.close();
高效缓冲字符流

加强普通的输入流、输出流,只是起一个加强作用,使用方式不变

构造方法
BufferedInputStream(字节输入流对象)
BufferedOutputStream(字节输出流对象)

FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("a_buffer.txt");

BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int a =0;
while((a = bis.read()) != -1) {
	bos.write(a);
}

bis.close();
bos.close();

高效原因?

BufferedInputStream 
	1)该类型底层准备了一个大小为8*1024大小的数组,调用者使用read()读取一个字节时,该对象一次读取8192个字节。
	2)将第一个字节返回给调用者,调用者第2次~第8192次再调用时,就不用去磁盘取数据了,直接从该数组取字节就好。
	3)当8192个字节读取完之后还需要取数据时,又在磁盘中挨着上次的8192个字节继续取数据即可
	
BufferedOutputStream 
	1)当使用write()方法写出字节数据时,先将写出的数据存储在自己的数组中(8*1024个字节大小),当写出够8192字节数据时,一起刷新
	2)如果没写出够8192个字节时,关闭流之前刷新
	
缓冲:底层有1204*8字节大小的数组,存储每次输入和输出的字节内容

刷新:将缓冲区中的数据,写进磁盘中

特有方法

flush(),刷新(将缓冲区中的数据,写进磁盘中)

	bos.write(b);
	bos.flush();
	bos.close();

字符流

概述

字符类底层就是字节流。加强了字节流。因为每次读、写时,不知道到底需要读、写几个字节(因为可能存在汉字、英文等夹杂着的情况),所以加强字节流,变为字符流。

所以
如果要表示中就需要连续读取两个字节,表示英文又只需要读取一个字节。


由于关联的具体设备位置,所以定义的也是抽象类。

PS:GBK编码表中,如果读取的字节是正数,那么就是英文;如果读取的第一个字节是负数,那么就是中文(中文由两个字节表示)

Reader

字符输入流的顶层抽象类

注意: Reader是一个抽象类,那么创建对象时,使用用子类创建

方法

int read())从当前字节输入流中,读取并返回一个字节,未读到返回-1
注意
	既然是字符,为什么是int类型?
	因为字符不能表示负数,无法指名没有读到字节的情况,所以,将字符前面加16个0,变成int
int read(char[] arr)
void close()
FileReader

Reader的子类,最常用的,与磁盘交互

构造方法

FileReader(File f)
FileReader(String path)指定路径的文件,封装为字符输入流
FileReader r = new FileReader("b.txt");
		//因为要表示没读到字符的状态,而char不能表示负数,所以定义int(前两个字节用0填充),-1表示没读到
		int a = 0;
		while((a = r.read()) != -1) {
			//read返回的是int类型的,所以需要转型
			System.out.print((char)a + " ");
		}

Writer

字符输出流的顶层抽象类

方法

write(int c)
write(char[] chs)
write(char[] chs, int offset, int len)从offset索引开始,共len个字符写出到指定文件

write(String str)将字符串编码形成字节数组之后,写出到目标文件去

write(String str, int offset, int len)
FileWriter

Writer的子类,最常用的,与磁盘交互

构造方法
FileWriter(String path)
FileWrtier(File f)
 	1)指定的路径不存在的,创建该文件
 	2)指定的路径是存在的,清空该文件

FileWriter(String path, boolean append)
FileWriter(File f, boolean append)
 	1)指定的路径不存在,自动创建
 	2)指定的路径存在,且append设置为true,则不会清空该文件,而是在后面追加
	FileWriter fw = new FileWriter("crush.txt");
	String str = "好久不见";
	fw.write(str, 0, 2);
	fw.close();

字符流文件拷贝

在这里插入图片描述
字符输入流
    字节——》GBK,GBK——》Unicode,Unicode——》int
字符输出流
    与字符输入流相反

普通的
	FileReader fr = new FileReader("c.txt");
	FileWriter fw = new FileWriter("c_copy.txt");
	
	int c;//每次读取到的字符
	while ((c = fr.read()) != -1) {
		fw.write(c);
	}
	
	fr.close();
	fw.close();

高效缓冲字符流

加强普通的输入流、输出流,只是起一个加强作用,使用方式不变

构造方法
BufferedReader(Reader r)
BufferedWriter(Writer w)

特有方法

1)BufferedReader
String readLine(),读取一整行,不包括换行符。返回null,表示没有可读的内容
注意:
	以换行符为每一次读取的结束符

2)BufferedWriter 	
newLine(),可以提供一个跨平台的换行符
	windows中为\r\n;linux中为\n
public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader("demo.java"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("demo_copy.java"));
		
		String a;
		while((a = br.readLine()) != null) {//读取一整行,没有下一行,返回null
//			System.out.println(a);
			bw.write(a);
			bw.newLine();//换行
		}
		br.close();
		bw.close();
	}

高效的原因同字节流一样

选择字节流、字符流

1)文件拷贝,最好使用高效缓冲字节流。因为字符类拷贝时,涉及很多操作,反而变慢了
2)关于字符的操作(除文件拷贝),使用字符流

转换流

编码表

字符集

含义:
	在字节和字符之间,相互转换时,查询的对应关系,就是编码表
常见编码表:
	GBK:定义了中文和英文的各种字符,一个英文使用一个字节表示,一个中文使用两个字节表示
	UTF-8:万国码提升形式,一个英文使用一个字节表示,一个中文使用三个字节表示
说明:
	一般在java的内存中,使用的编码都是Unicode编码,因为java中的字符都使用两个字节来表示
	在java的内存以外,会使用其他的编码形式,具体使用哪种,取决于文件的要求

转换流

就是将字节流包装成了字符流对象,可以直接操作字符

Reader									字符输入流的顶层抽象父类
 	 	InputStreamReader 	 	 	 	能自主确定解码形式的字符输入流
 	 	
 	 		构造方法
 	 		InputStreamReader(InputStream is)将指定的字节流转成字符流,按照平台默认编码表解码
			InputStreamReader(InputStream is, String charSetName)当前转换流,按照指定的字符集来解码


 	 	 	FileReader 	 	 	 	 	使用平台默认解码的操作文件的字符输入流


 	Writer 	 	 	 	 	 	 	 	字符输出流的顶层抽象父类
 	 	OutputStreamWriter 	 	 	 	能自主确定编码形式的字符输出流
 	 	
 	 		构造方法
 	 			OutputStreamWriter(OutputStream os)将指定的字节输出流转换成字符输出流,按照平台默认编码表编码
				OutputStreamWriter(OutputStream os, String charSetName)当前转换流,按照指定的字符集来编码
				
 	 	 	FileWriter 	 	 	 	 	使用平台默认编码的操作文件的字符输出流

一、输入流关联键盘

	InputStream is = System.in;
	InputStreamReader isr = new InputStreamReader(is);
	BufferedReader br = new BufferedReader(isr);
	System.out.println(br.readLine());

二、将一个uft8编码的文件内容,拷贝到一个gbk编码的文件中(eclipse中默认的编码表是Unicode,但是打开txt文本时,使用的编辑器默认是GBk解码的)

	private static void test_utf8_copy_gbk() throws  IOException {
//		InputStream is = new FileInputStream("gbk_utf8.txt");
//		InputStreamReader fr = new InputStreamReader(is,"utf8");
//		OutputStream fw = new FileOutputStream("gbk_utf8_copy.txt");
//		OutputStreamWriter osw = new OutputStreamWriter(fw);
		InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk_utf8.txt"),"utf8");
		FileWriter fw = new FileWriter("gbk_utf8_copy.txt");
		
		int a;
		while((a = isr.read()) != -1) {
			fw.write(a);
		}
		
		isr.close();
		fw.close();
	}

三、将一个gbk编码的文件的内容,拷贝到一个utf8编码的文件中(eclipse中默认的编码表是Unicode,但是打开txt文本时,使用的编辑器默认是GBk解码的)

public static void gbk_copy_utf8() throws IOException {
		//解码格式为GBK可以不写???
		FileReader fr = new FileReader("gbk_utf8_copy.txt");
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk_utf8.txt"), "utf8");
		int a;
		while((a = fr.read()) != -1) {
			osw.write(a);
		}
		fr.close();
		osw.close();
		
	}

【注意】

一、读取、写出格式
读取时,解码格式必须和源文件的编码格式相同
写出时,编码格式必须和目标文件的编码格式相同

二、编码和解码
将字符转换为字节的方式称为编码(A——》65)
将字节转换为字符的方式称为解码(65——》A)


练习


疑惑

1、既然eclipse中文本默认是GBK编码,而java代码默认是Unicode编码,那么将GBK编码的文本拷贝到UTF8编码的文本时,读取中英文汉字混合的情况的时候为何没有乱码???

猜测

GBK——》Unicode
英文:1个字节——》2个字节,前面加8个0
汉字:2个字节——》2个字节
Unicode——》UTF8
英文:2个字节——》1个字节,去掉8个0
汉字:2个字节——》3个字节,前面加8个0

2、格式为GBK可以不写???
FileReader fr = new FileReader(“gbk_utf8_copy.txt”);
FileWriter fw = new FileWriter(“gbk_utf8_copy.txt”);

3、编码是字符变字节,为何读取时叫解码;写出时叫编码???

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈年_H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值