Java基础(12)----IO之字节流和字符流

IO

概述与分类

  • IO流介绍

    • IO:输入/输出(Input/Output)
    • 流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输
    • IO流就是用来处理设备间数据传输问题的。常见的应用:文件复制;文件上传;文件下载
  • IO流的分类

    • 按照数据的流向

      • 输入流:读数据
      • 输出流:写数据
    • 按照数据类型来分

      • 字节流

        • 字节输入流
        • 字节输出流
      • 字符流

        • 字符输入流
        • 字符输出流
    • 按照角色分类:按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类

      • 节点流:可以从或向一个特定的地方(节点)读写数据

        字节流和字符流都是节点流

      • 处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。比如缓冲流。

        处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多

  • IO流的使用场景

    • 如果操作的是纯文本文件,优先使用字符流
    • 如果操作的是图片、视频、音频等二进制文件优先使用字节流
    • 如果不确定文件类型,优先使用字节流。字节流是万能的流

关流:先用后关,后用先关

字节流

  • new FileOutputStream(“name”)会产生一个新文件
  • new FileInputStream(“name”)不会创建新文件,若文件不存在会报错;
  • new File(“name”)不会创建新文件,在new File()之后需要调用createNewFile()才能创建新文件;
  • file=new File(“name”)+new FileInputStream(file),不会创建新文件,若文件不存在也会报错;
  • file=new File(“name”)+new FileOutputStream(file),会创建新文件。

定义:字节流是以8位字节为数据单元的IO流

  • 字节流抽象基类
作用
InputStream该抽象类表示字节输入流的所有类的超类(数据由数据源流到程序中,为读)
OutputStream该抽象类表示字节输出流的所有类的超类(数据由程序流到数据源中,为写)

子类名特点:子类名称都是以其父类名作为子类名的后缀

字节输出流
  • FileOutputStream(String name):创建文件输出流以指定的名称写入文件

使用字节输出流写数据的步骤

  • 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
  • 调用字节输出流对象的写数据方法
  • 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
常用方法
  • 写数据的方法分类
方法名说明
void write(int b)将指定的字节写入此文件输出流 一次写一个字节数据
void write(byte[] b)将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据
void write(byte[] b, int off, int len)将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据
void close()关闭输出流
  • 代码示例
@Test
	public void test2() throws Exception {
		//以字节为单位向文件中写入内容
		FileOutputStream fos = new FileOutputStream("E:\\testNomal\\FileTest\\三国演义2.txt");
		//将字符串转换成为字节数组写入文件
		fos.write("helloJava".getBytes());
		System.out.println();
		fos.close();
	}
/*
参数file后面还有一个可选参数append,
当append为true时,从文件末尾写入数据。

当append为false时,从文件头部写入,就是覆盖,默认为false。

改成了 FileOutputStream fos = new FileOutputStream(file,true);

过后写的数据就不会被覆盖。
*/

字节输入流
  • 字节输入流

    • FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream ,该文件由文件系统中的路径名name命名
  • 字节输入流读取数据的步骤

    • 创建字节输入流对象
    • 调用字节输入流对象的读数据方法
    • 释放资源
常用方法
方法内容
int read()读取单个字节到程序,以int型返回所读取的字节数据,返回值为-1表示读取到了文件的末尾
int read(byte[] b)读取多个字节到程序,并存储在字节数组中,返回实际读取的字节数,返回值为-1表示读取到了文件的末尾
void read(byte[] b, int off, int len)将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输入流 一次读一个字节数组的部分数据
void close()关闭输入流
  • 代码示例
public class TestFileInputStream {
	
	/**
	 * 通过字节输入流(FileInputStream),读取电脑中文件的内容
	 * @throws IOException 
	 */
	@Test
	public void test1() throws IOException{
		//以字节为单位读取内容
		FileInputStream fis = new FileInputStream("E:\\testNomal\\FileTest\\三国演义1.txt");
		//读取文件内容,read(),如果返回值不是-1,就说明还存在未读的字节
		int i = 0;
		String sum = "";
		while((i=fis.read()) != -1){
			sum += (char)i;
		}
		//乱码原因:因为现在是字节流,而我文档里面有中文,中文是字符,占两个字节,所以乱码,改成全是英文就可以解决这个问题
		System.out.println(sum);//??????°??ú???????ú????????????????
		
		fis.close();
	}

}
  • 代码实例

*案例题目描述:*

完成学生封装,保存数据到文件

*根据题目要求完成*

  1. 封装学生类Student,包含属性 学号id,姓名name,年龄age,提供带参数的构造方法(20分)

  2. 创建四个学生对象,把四个对象存放到List集合中。(30分)

Id:1 name:张三 age:18

Id:2 name:李四 age:19

Id:3 name:王雷 age:28

Id:4 name:李丽 age:39

  1. 遍历集合中所有的数据,id为偶数的打印在控制台上(20分)

  2. id为奇数输出到D盘的test.txt文本文件当中(20)

*其他*

  1. 要求代码每个方法都有注释。(10分)

student类

public class Student {
	private int id;
	private String name;
	private int age;
	public Student(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
}
  • 测试类
@Test
   public void test1() throws Exception{
   	//因为在这个地方还没有学习字符流,所以name不能是中文
   	List<Student> ls = new ArrayList<Student>();
   	ls.add(new Student(1,"ary",17));
   	ls.add(new Student(2,"Tom",16));
   	ls.add(new Student(3,"Amy",18));
   	ls.add(new Student(4,"Lily",19));
   	
   	String info = "";
   	//遍历集合容器
   	for(Student s : ls){
   		if(s.getAge()%2==0){//id为偶数的打印在控制台
   			System.out.println(s);
   		}else{//id为奇数输出到D盘的test.txt文本文件
   			info += s+"\r\n";//\r\n是回车换行的转义字符
   		}
   	}
   	FileOutputStream fos = new FileOutputStream("E:\\testNomal\\FileTest\\test.txt");
   	//将内容写入文件
   	fos.write(info.getBytes());
   	fos.close();
   	System.out.println("一切结束");
   }
/*
Student [id=2, name=Tom, age=16]
Student [id=4, name=Lily, age=19]
一切结束

*/
字节缓冲流

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

  • 字节缓冲流介绍:以提高读写性能为目的的,具备缓冲区的处理流

    • BufferOutputStream:该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节而导致底层系统的调用

    • BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次输入很多字节

  • 构造方法:

    方法名说明
    BufferedOutputStream(OutputStream out)创建字节缓冲输出流对象
    BufferedInputStream(InputStream in)创建字节缓冲输入流对象

方法跟上面的差不多,就是换了一个类名,在这里就不演示了

	@Test
	public void test7() throws Exception{
		//创建一个FileInputStream对象,以字节为单位读取内容
		FileInputStream fis = new FileInputStream("E:\\testNomal\\FileTest\\三国演义1.txt");
		BufferedInputStream bi = new BufferedInputStream(fis,1024);//1024:缓冲区大小
		//读取内容
		int i;
		String msg = null;
		if((i=bi.read()) != -1){
			msg += (char)i;
		}
		System.out.println(msg);
		bi.close();
		fis.close();
	}
	@Test
	public void test8() throws Exception{
		//创建一个FileInputStream对象,以字节为单位读取内容
		FileOutputStream fos = new FileOutputStream("E:\\testNomal\\FileTest\\三国演义1.txt");
		BufferedOutputStream bos = new BufferedOutputStream(fos,1024);//1024:缓冲区大小
		bos.write("aiuhdia".getBytes());
		
		bos.close();
		fos.close();
	}

编码表

  • 什么是字符集

    是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等

    l计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等

  • 常见的字符集

    • ASCII字符集:

      lASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)

      基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等

    • GBXXX字符集:

      GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等

    • Unicode字符集:

      UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码

      编码规则:

      128个US-ASCII字符,只需一个字节编码

      拉丁文等字符,需要二个字节编码

      大部分常用字(含中文),使用三个字节编码

      其他极少使用的Unicode辅助字符,使用四字节编码

字符流

  • 字符流的介绍

    由于字节流操作中文不是特别的方便,所以Java就提供字符流

    字符流 = 字节流 + 编码表

  • 中文的字节存储方式

    用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文。

    汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数

解决编码问题
字符串
  • 相关方法
方法名说明
byte[] getBytes()使用平台的默认字符集将该 String编码为一系列字节
byte[] getBytes(String charsetName)使用指定的字符集将该 String编码为一系列字节
String(byte[] bytes)使用平台的默认字符集解码指定的字节数组来创建字符串
String(byte[] bytes, String charsetName)通过指定的字符集解码指定的字节数组来创建字符串
	@Test
	public void test4() throws UnsupportedEncodingException{
		//我项目的编码集是UTF-8
		String s = "我爱中国";
		System.out.println(s);//我爱中国
		byte[] bytes = s.getBytes("GBK");
		System.out.println(Arrays.toString(bytes));//[-50, -46, -80, -82, -42, -48, -71, -6]
		System.out.println(bytes);//[B@14514713
		//在这里的bytes这个位置只能是变量,不是的话会报错
		String s1 = new String(bytes,"GBK");
		System.out.println(s1);//我爱中国
		String s2 = new String(bytes,"UTF-8");
		System.out.println(s2);//????й?
	}
字符流
  • 字符流中和编码解码问题相关的两个类

    • InputStreamReader:是从字节流到字符流的桥梁

      它读取字节,并使用指定的编码将其解码为字符

      它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

    • OutputStreamWriter:是从字符流到字节流的桥梁

      是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节

      它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

  • 构造方法

    方法名说明
    InputStreamReader(InputStream in)使用默认字符编码创建InputStreamReader对象
    InputStreamReader(InputStream in,String chatset)使用指定的字符编码创建InputStreamReader对象
    OutputStreamWriter(OutputStream out)使用默认字符编码创建OutputStreamWriter对象
    OutputStreamWriter(OutputStream out,String charset)使用指定的字符编码创建OutputStreamWriter对象
@Test
	public void test5() throws Exception{
     OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\testNomal\\FileTest\\FileWriterTestUnic.txt"),"GBK");
     osw.write("我爱中国");
     osw.close();
     
     InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\testNomal\\FileTest\\FileWriterTestUnic.txt"),"GBK");
   //一次读取一个字符数据
     int ch;
     while ((ch=isr.read())!=-1) {
         System.out.println((char)ch);
     }
     isr.close();
     
	}
/*
我
爱
中
国
*/
字符流读数据
常用方法
方法名说明
int read()一次读一个字符数据
int read(char[] cbuf)一次读一个字符数组数据
  • 代码演示
@Test
	public void test2() throws Exception{
		//创建实例化FileReader对象
		FileReader fr = new FileReader("E:\\testNomal\\FileTest\\三国演义3.txt");
		int i = 0;
		String mess = "";
		while((i=fr.read()) != -1){
			mess += (char)i;
			//如果没有char的话,输出的是一长串数字
			//6553365533655331016655336553349765533655336553365533108165533655336553365533140165533655331780655336553365533185565533881655336553365533655336553365533655336553365533655336553342356553365533
		}
		System.out.println(mess);
		//仍然乱码:???????????й?????????????????????????*#??

		
		//原因:文件保存的编码和现在Eclipse里面的编码集不一样,所以乱码,将文件和项目的编码集改为一样的
		fr.close();
	}
字符流写数据
常用方法
方法名说明
void write(int c)写一个字符
void write(char[] cbuf)写入一个字符数组
void write(char[] cbuf, int off, int len)写入字符数组的一部分
void write(String str)写一个字符串
void write(String str, int off, int len)写一个字符串的一部分
刷新和关闭的方法
方法名说明
flush()刷新流,之后还可以继续写数据
close()关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
  • 代码演示
@Test
	public void test3() throws Exception{
		//创建一个FileWriter对象,以字符为数据单元想文件中写入内容
		FileWriter fw = new FileWriter("E:\\testNomal\\FileTest\\FileWriterTest.txt");
		//通过writer方法写入内容
		fw.write("是非成败转头空");
		//关流
		fw.close();
	}
  • 综合案例

实现注册-——登录——退出功能

@Test
	public void test6() throws Exception{
		boolean flag = true;
		Scanner sc  = new Scanner(System.in);
		int i = 0;
		while(flag){
			System.out.println("请选择功能:1.注册;2:登录;3:退出");
			i = sc.nextInt();
			switch(i){
			case 1:
				System.out.println("请输入用户名:");
				String name = sc.next();
				System.out.println("请输入密码:");
				String pwd = sc.next();
				
				FileWriter fw = new FileWriter("E:\\testNomal\\FileTest\\login\\"+name+".txt");
				fw.write(pwd);
				fw.close();
				break;
			case 2:
				File fi = new File("E:\\testNomal\\FileTest\\login");
				File[] lf = fi.listFiles();
				System.out.println("请输入用户名:");
				String Lname = sc.next();
				System.out.println("请输入密码:");
				String Lpwd = sc.next();
				
				boolean isOk = false;
				for(File f : lf){
					if((Lname+".txt").equals(f.getName())){
						isOk = true;
					}
				}
				if(isOk){
					//读文件内容,使用FileReader
					FileReader fr = new FileReader("E:\\testNomal\\FileTest\\login\\"+Lname+".txt");
					int j = 0;
					String msg = "";
					while((j=fr.read()) != -1){
						msg += (char)j;
					}
					//关流
					fr.close();
					if(Lpwd.equals(msg)){
						System.out.println("登录成功");
					}else{
						System.out.println("密码错误,登录失败");
					}
				}else{
					System.out.println("系统中没有该用户,请先注册!");
				}
				break;
			case 3:
				flag = false;
				break;
			default:
				System.out.println("输入错误,请重新输入!");
			}
		}
	}
字符缓冲流

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

  • 字符缓冲流介绍

    • BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
    • BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途
  • 构造方法

方法名说明
BufferedWriter(Writer out)创建字符缓冲输出流对象
BufferedReader(Reader in)创建字符缓冲输入流对象
特有方法
  • BufferedWriter
方法名说明
void newLine()写一行行分隔符,行分隔符字符串由系统属性定义
  • BufferedReader:
方法名说明
String readLine()读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的。如果结尾已经到达,则为null
  • 代码测试
	@Test
	public void test9() throws Exception{
		//创建实例化FileReader对象
		FileReader fr = new FileReader("E:\\testNomal\\FileTest\\三国演义3.txt");
		BufferedReader br = new BufferedReader(fr,1024);//1024:缓冲区大小
		String msg = "";
//		int i=0;
		String i="";
		while((i=br.readLine()) != null){
//			msg += (char)i;
			msg += i;
		}
		System.out.println(msg);
		br.close();
		fr.close();
	}
	@Test 
	public void test10() throws Exception{
		FileWriter fw = new FileWriter("E:\\testNomal\\FileTest\\三国演义3.txt");
		BufferedWriter bw = new BufferedWriter(fw,1024);//1024:缓冲区大小
		bw.write("折扣价和空间和");
//		bw.write("\r\n");
		bw.newLine();//与上一行代码意义差不多
		bw.write("我不是药神");
		
		bw.close();
		fw.close();
	}

小结

  • 字节流

  • 字符流

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

?abc!

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

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

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

打赏作者

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

抵扣说明:

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

余额充值