day11-IO流

2024-05-12 17:28
文章标签 day11 io

本文主要是介绍day11-IO流,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

IO流

1 IO流的概述和分类

1.1学习IO流的目的?

1,将数据写到文件中,实现数据永久化存储

2,读取文件中已经存在的数据

1.2 IO流概述

其中:I表示intput,是数据从硬盘进内存的过程,称之为读。

O表示output,是数据从内存到硬盘的过程。称之为写。

1.3 思考一个问题?

在数据传输的过程中,是谁在读?是谁在写?这个参照物是谁?

IO的数据传输,可以看做是一种数据的流动,按照流动的方向,以内存为参照物,进行读写操作。

简单来说:内存在读,内存在写。

在这里插入图片描述

1.4 IO流的分类

1.按流向分—输入流 输出流

在这里插入图片描述

2.按数据类型分–字节流 字符流

字节流 操作所有类型的文件 (包括音频视频图片等)

字符流 只能操作纯文本文件 (包括java文件,txt文件等)

在这里插入图片描述

​ 一般来说,IO流的分类是按照数据类型来分的

1.5 IO流的技术选型

那我们在用流读取内容的时候,应该如何选择 流的格式呢?

那我们不得不提到一个知识

什么是纯文本文件?

用windows记事本打开能读的懂,那么这样的文件就是纯文本文件。

在这里插入图片描述

思考:office文件可以用字符流操作吗? no! 因为不是纯文本文件

思考:下面这些文件分别可以用什么流操作? 字符 字节 字节 字节

在这里插入图片描述

1.6 总结:IO流具体分类和使用场景

  • 按照数据的流向

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

    • 字节流
      • 字节输入流
      • 字节输出流
    • 字符流
      • 字符输入流
      • 字符输出流
  • IO流的使用场景

    • 如果操作的是纯文本文件(可以用记事本打开),优先使用字符流
    • 如果操作的是图片、视频、音频等二进制文件,优先使用字节流
    • 如果不确定文件类型,优先使用字节流.字节流是万能的流

2.字节流

操作所有类型的文件 包括音频视频图片等

2.1 字节流的创建

image-20220426190319349
字节流抽象基类- InputStream:这个抽象类是表示字节输入流的所有类的超类。- OutputStream:这个抽象类是表示字节输出流的所有类的超类。- 子类名特点:子类名称都是以其父类名作为子类名的后缀。
字节输入/出流- FileInputStream(String name):创建文件输入流以指定的名称读取文件。- FileOutputStream(String name):创建文件输出流以指定的名称写入文件。

案例引入:案例 往images文件夹中的a.txt文件中写入数据

2.2 字节流写数据步骤

1.创建字节输出流对象

注意事项:
如果文件不存在,就创建。
2.写数据

注意事项:
写出的整数,实际上写到文件中,是在ASCII码表中那个字符。
3.释放资源

注意事项:
每次使用完流必须要释放资源。

注意点:

  • 1.如果文件不存在,会帮我们创建2.如果文件存在,会把文件覆盖。
  • 传递一个整数,实际上写到文件中,是在ASCII码表中那个字符。
  • 每次使用完流必须要释放资源。

代码展示:

FileOutputStream fos = new FileOutputStream("E:/file/image/abc.txt");
fos.write(97);//写的是 char中的类型
fos.close();
FileInputStream fis = new FileInputStream("E:/file/image/a.txt");
int read = fis.read();
System.out.println(read);
fos.close();

2.3 字节流写数据的3种方式

方法名说明
void write(int b)一次写一个字节数据
void write(byte[] b)一次写一个字节数组数据
void write(byte[] b, int off, int len)一次写一个字节数组的部分数据

不需要记住 百度直接搜索ASCALL码表即可

代码演示

//    fos.write(97);//写的是 char中的类型
//        fos.write(98);
//        fos.write(99);
//        fos.write(100);
//        fos.write(101);//abcde//字节数组byte[]bytes = {97,98,99,100,101};
//        fos.write(bytes);//abcde//void write(byte[] b, int off, int len):将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流fos.write(bytes,2,2);//off 可以认为索引 从0开始 截取长度为len的内容输出到文件中fos.close();

2.4 字节流写数据的两个小问题

字节流写数据如何实现换行呢?
写完数据后,加换行符

windows:\r\n 单写\r \n 也可以

linux:\n

mac:\r

字节流写数据如何实现追加写入呢?

public FileOutputStream(String name,boolean append)

创建文件输出流以指定的名称写入文件。如果第二个参数为true ,不会清空文件里面的内容

FileOutputStream fos = new FileOutputStream("E:/file/image/abc.txt",true);
fos.write(97);//写的是 char中的类型
fos.write("\r\n".getBytes());//换行
fos.write(98);
fos.write("\r\n".getBytes());//换行
fos.write(99);
fos.write("\r\n".getBytes());//换行
fos.write(100);
fos.write("\r\n".getBytes());//换行
fos.write(101);//abcde
fos.write("\r\n".getBytes());//换行
fos.close();

2.5 字节流写数据加try…catch异常处理

思考:那么我们如何操作才能让close方法一定执行呢?

finally:在异常处理时提供finally块来执行所有清除操作。比如说IO流中的释放资源

特点:被finally控制的语句一定会执行,除非JVM退出

异常处理标准格式:try….catch…finally

代码展示:

FileOutputStream fos = null;
try {fos = new FileOutputStream("E:/file/image/abc.txt",true);fos.write(97);//写的是 char中的类型fos.write("\r\n".getBytes());//换行fos.write(98);fos.write("\r\n".getBytes());//换行fos.write(99);fos.write("\r\n".getBytes());//换行fos.write(100);fos.write("\r\n".getBytes());//换行fos.write(101);//abcdefos.write("\r\n".getBytes());//换行
} catch (FileNotFoundException e) {e.printStackTrace();
} catch (IOException e) {e.printStackTrace();
}finally {try {fos.close();} catch (IOException e) {e.printStackTrace();}
}

2.5 小结:字节流读数据 一次读一个字节

步骤:
1. 创建字节输出流对象
文件不存在,就创建。
文件存在就清空。如果不想被清空则加true
2. 写数据
可以写一个字节,写一个字节数组,写一个字节数组的一部分
写一个回车换行:\r\n
3. 释放资源注意事项:如果文件不存在,就直接报错。
注意事项:读出来的是文件中数据的码表值。 a  97
注意事项:每次使用完流必须要释放资源。

2.6 案例 往images文件夹中的a.txt文件中写入数据

往images文件夹中的a.txt文件中写入数据(只能写英文,中文乱码) I love you HuiHui

  1. 创建一个文件对象
  2. 判断一下这个文件存在否,不存在创建
  3. 创建一个输出流
  4. 创建数据,写数据
  5. 关闭输出流

代码:

public static void main(String[] args) {FileOutputStream fos = null;try {File file = new File("E:/file/image/a.txt");if (file != null && file.length() > 0) {fos = new FileOutputStream(file);String s1 = " I love you HuiHui";byte[] bytes = s1.getBytes();fos.write(bytes);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}

2.8 字节流读取数据

在这里插入图片描述

2.9 字节流读取数据步骤

案例引入:读取images文件夹中的a.txt文件中数据

如何读取呢?

1.创建字节输入流对象。

注意事项:
如果文件存在则读取。
2.读取数据

注意事项:
读取的是字节的ASCII码需要转换为char类型
读取中文会乱码。
3.释放资源

注意事项:
每次使用完流必须要释放资源。

2.10 字节流读取数据的2种方式

方法名说明
int read()一次读取一个字节数据
int read(byte[] b)一次读一个字节组数据,把数据封装到参数b中,返回值为本次读取到的字节个数

读取到最后,返回值为-1

注意:read()方法在一次使用里面最好只调用一次

示例代码:

public static void main(String[] args) {FileInputStream fis = null;byte[]bytes = {97,98,99};try {fis = new FileInputStream("E:/file/image/a.txt");// 读一个字节//int read = fis.read();//System.out.println(read);//向下继续读取字节 读取多个//int read1 = fis.read();// System.out.println(read1);// 循环读取int b;//fis.read() 读到结果返回,读不到就返回一个-1.// b = 正常值 如果为-1 -1占了两个字节            while ((b = fis.read()) != -1) {//fis.read(bytes,0,4)System.out.println((char)b);}//        System.out.println(fis.read(bytes));//3 可以设置字节数组 每次读取字节数组的字节个数
//        System.out.println(fis.read(bytes));//3
//        System.out.println(fis.read(bytes));//3
//        while ((b=fis.read(bytes))!=-1){
//            System.out.println((char) b);//乱码
//        }} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}

英文和数字占两个字节

2.11 案例 读取images文件夹中的a.txt文件中数据

**需求:**读取images文件夹中的a.txt文件中的数据(只能写英文,中文乱码)

步骤:

  1. 创建一个文件对象
  2. 判断一下这个文件存在否
  3. 创建一个输入流
  4. 读取数据
  5. 关闭输入流

代码展示:

public static void main(String[] args) {FileInputStream fis = null;try {fis = new FileInputStream("E:/file/image/a.txt");int b ;while ((b = fis.read() )!= -1) {System.out.println((char) b);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {fis.close();} catch (IOException e) {e.printStackTrace();}}
}

2.12 案例:复制文件

需求:把“E:\images\a.png”复制到当前模块下
分析:
复制文件,其实就把文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)
数据源:
E:\images\a.png ---- 读数据 — FileInputStream
目的地:
模块名称\copy.png — 写数据 — FileOutputStream

代码实现:

public static void main(String[] args) {//创建输入流FileInputStream fis = null;//创建输出流FileOutputStream fos = null;try {fis = new FileInputStream("E:\\file\\image\\a.txt");fos = new FileOutputStream("src\\com\\itgaohe\\123.txt");//读取 本地的 文件int b;while ((b = fis.read()) != -1) {//写出到 当前模块中fos.write(b);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {try {fos.close();} catch (IOException e) {e.printStackTrace();}try {fis.close();} catch (IOException e) {e.printStackTrace();}}
}

2.13 思考问题

思考:如果操作的文件过大,那么速度会不会有影响?

一个字:慢!!!

在这里插入图片描述

问题:在读取的时候 一次只能 一个字节字节的读取;写出的时候也只能一次写一个字节数据的写出。

为了改变这个问题

提高拷贝速度的解决方案

为了解决速度问题,

1.字节流通过创建字节数组,可以一次读写多个数据。
2.一次读一个字节数组的方法:public int read(byte[] b)从输入流读取最多b.length个字节的数据
返回的是读入缓冲区的总字节数,也就是实际的读取字节个数

在这里插入图片描述

代码展示:

//读取 本地的 文件
int b = -1;
byte[]bytes = new byte[1024];
while ((b = fis.read(bytes)) != -1) {//写出到 当前模块中fos.write(bytes,0,b);
}

2.14 字节流缓冲流

字节缓冲流:
BufferOutputStream:缓冲输出流
BufferedInputStream:缓冲输入流

构造方法:
字节缓冲输出流:BufferedOutputStream​(OutputStream out)
字节缓冲输入流:BufferedInputStream​(InputStream in)

为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?
字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作

private static int DEFAULT_BUFFER_SIZE = 8192; 默认创建长度为8192的字符缓冲数组

在这里插入图片描述

在这里插入图片描述

完善

在这里插入图片描述

在这里插入图片描述

代码演示:

 public static void main(String[] args) throws IOException {//ppt doc txt jpg png mp4 都可以读BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/file/image/IO.pptx"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:/file/image/IO2.pptx"));int len = -1;byte[] bytes = new byte[1024];while ((len = bis.read(bytes)) != -1) {bos.write(bytes,0,len);}bis.close();bos.close();}

2.15 案例:复制视频

需求:把“E:\itgaohe\a.avi”复制到模块目录下的“b.avi”
思路:
根据数据源创建字节输入流对象
根据目的地创建字节输出流对象
读写数据,复制视频
释放资源

代码展示:

public static void main(String[] args) throws IOException {BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/file/image/guangzhou.mp4"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src/com/itgaohe/guangzhou2.mp4"));int len = -1;byte[] bytes = new byte[1024];while ((len = bis.read(bytes)) != -1) {bos.write(bytes,0,len);}bis.close();bos.close();
}

2.16 小结

方法名说明
void write(int b)一次写一个字节数据
void write(byte[] b)一次写一个字节数组数据
void write(byte[] b, int off, int len)一次写一个字节数组的部分数据
int read()一次读取一个字节数据
int read(byte[] b)一次读一个字节组数据,把数据封装到参数b中,返回值为本次读取到的字节个数
操作所有类型的文件 没有读取到位-1 字节缓冲流:可以提高读写效率

3.字符流

3.1 思考:为什么要学习字符流

把文件中的数据读取到内存时,如果此时文件中出现了中文,那么字节流就会出现乱码现象。所以纯文本的文件,我们就需要使用字符流来进行操作。

为什么字节流读取纯文本文件,可能会出现乱码?

其实这个跟 计算机编码规则有关

 public static void main(String[] args) throws IOException {//创建输入流FileInputStream fis = null;fis = new FileInputStream("E:\\file\\123.txt");//读取 本地的 文件int b = -1;while ((b = fis.read()) != -1) {//写出到 当前模块中
//                System.out.println(b);//打印12个字节System.out.println((char) b);// 文件中加入中文 出现中文乱码}fis.close();}

那具体是遵循什么规则呢?

3.2 编码表

基础知识:

计算机中储存的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
按照某种规则,将字符存储到计算机中,称为编码
按照同样的规则,将存储在计算机中的二进制数解析显示出来,称为解码
编码和解码的方式必须一致,否则会导致乱码。
简单理解:
存储一个字符a,首先需在码表中查到对应的数字是97,然后按照转换成二进制的规则进行存储。
读取的时候,先把二进制解析出来,再转成97,通过97查找到对应的字符是a。

ASCII字符集:
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字,大小写字符和一些常见的标点符号。
注意:ASCII码表中是没有中文的。
GBKwindow系统默认的码表。兼容ASCII码表,也包含了21003个汉字,并支持繁体汉字以及部分日韩文字。
注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。

Unicode码表:

由国际组织ISO 制定,是统一的万国码,计算机科学领域里的一项业界标准,容纳世界上大多数国家的所有常见文字和符号。
但是因为表示的字符太多,所以Unicode码表中的数字不是直接以二进制的形式存储到计算机的,会先通过UTF-7,UTF-7.5,UTF-8,UTF-16,以及 UTF-32的编码方式再存储到计算机,其中最为常见的就是UTF-8。
注意: Unicode是万国码,以UTF-8编码后一个中文以三个字节的形式存储

编码表小结

在这里插入图片描述

  • 什么是字符集

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

    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辅助字符,使用四字节编码

3.3 汉字存储和展示过程解析

我们先来了解一下 汉字的存储过程 再去看为什么字节流读取的时候可能会出现乱码

在这里插入图片描述

重点:windows默认使用码表为:GBK,一个字符两个字节。
idea和以后工作默认使用Unicode的UTF-8编解码格式,一个中文三个字节。

3.4 字符串中的编码解码问题

那对于汉字/字符串读取的内容 他是如何进行存储的呢?

编码:
byte[] getBytes​():使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中

byte[] getBytes​(String charsetName):使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中

解码:

String​(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的 String

String​(byte[] bytes, String charsetName):通过指定的字符集解码指定的字节数组来构造新的 String

代码展示:

String s = "山东高合";
byte[] bytes = s.getBytes();//UTF-8默认
System.out.println(Arrays.toString(bytes));byte[] bytes1 = s.getBytes("UTF-8");默认为UTF-8  一个中文3个字节
System.out.println(Arrays.toString(bytes1));byte[] bytes2 = s.getBytes("GBK");
System.out.println(Arrays.toString(bytes2));//一个中文2个字节byte[]bytes3 = {-27, -79, -79, -28, -72, -100, -23, -85, -104, -27, -112, -120};
String s1 = new String(bytes3);
System.out.println(s1);String s2 = new String(bytes3,"GBK");//灞变笢楂樺悎 乱码 原因:编码和解码格式不一致!
System.out.println(s2);byte[]bytes4 = {-55, -67, -74, -85, -72, -33, -70, -49};
String s4 = new String(bytes4,"GBK");
System.out.println(s4);

结论:不管是那种流,只要支持中文,以相同的编码格式读取、解析就能得到中文。

​ //编码和解码 - 码表不一致。乱码。

例如:GBK 读取 GBK 输出

​ UTF-8读取 UTF-8输出

3.5 为什么字节流读取纯文本文件,可能会出现乱码?

因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以就会出现乱码问题。

在这里插入图片描述

3.6 字符流读取中文的过程

为了解决字节流读取纯文本文件,可能会出现乱码的问题。我们提供字符流来读取中文 我们来看一下他具体是怎么存储的

字符流 = 字节流 + 编码表

基础知识: 不管是在哪张码表中,中文的第一个字节一定是负数。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.7 字符流写数据步骤:

1.创建字符输出流对象
注意事项:
如果文件不存在,就创建。但是要保证父级路径存在。
如果文件存在就清空。

2.写数据
注意事项:
1,写出int类型的整数,实际写出的是整数在码表上对应的字母。
2,写出字符串数据,是把字符串本身原样写出。

fw.write(97);

3.释放资源
注意事项:
每次使用完流必须要释放资源。

3.8 字符流的创建

接下来我们就用字符流 来解决中文乱码的问题。

1. FileWriter字符输出流

构造器说明
public FileWriter(File file)创建字符输出流管道与源文件对象接通
public FileWriter(File file,boolean append)创建字符输出流管道与源文件对象接通,可追加数据
public FileWriter(String filepath)创建字符输出流管道与源文件路径接通
public FileWriter(String filepath,boolean append)创建字符输出流管道与源文件路径接通,可追加数据

2.FileReader字符输入流

构造器说明
public FileReader(File file)创建字符输入流管道与源文件对象接通
public FileReader(String pathname)创建字符输入流管道与源文件路径接通

代码:

   FileReader fr = new FileReader("src/abc.txt");FileWriter fw = new FileWriter("src/cde.txt");int len ;while ((len=fr.read())!=-1){fw.write(len);}fw.close();fr.close();
}

3.9 字符流写数据的5种方式

方法名说明
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)写一个字符串的一部分

3.10 字符流读和关闭流刷新流

方法名说明
flush()刷新流,还可以继续写数据
close()关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
int read()一次读一个字符数据
int read(char[] cbuf)一次读一个字符数组数据

flush() 方法的使用原因:flush本意是冲刷,这个方法大概取自它引申义冲马桶的意思,马桶有个池子,你往里面扔东西,会暂时保存在池子里,只有你放水冲下去,东西才会进入下水道。

专业术语叫缓冲区 当你print或者write的时候,会暂时保存在缓冲区 当你直接调用close()方法关闭流的时候,在流的通道中 还有缓存 没有清理掉,刷新一下,方便下一次的使用。就像刷马桶一样。所以应该在关闭读写流之前先flush()。

代码展示: 用字符流读写文件

    public static void main(String[] args) {FileReader fr = null;FileWriter fw = null;try {//读fr = new FileReader("E:/file/dogdaily.txt");//写fw = new FileWriter("src/com/itgaohe/my.txt");int c = -1;while ((c = fr.read()) != -1) {fw.write(c);}//如果读取的字符数量很多 则用如下格式
//            char[] chars = new char[1024];
//            int c = -1;
//            while ((c = fr.read(chars)) != -1) {
//                fw.write(chars);
//            }} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {fr.close();} catch (IOException e) {e.printStackTrace();}try {fw.close();} catch (IOException e) {e.printStackTrace();}}}

3.11 案例:使用字符流保存键盘录入的数据

需求:将用户键盘录入的用户名和密码保存到本地实现永久化存储。
步骤:
用户键盘录入用户名
将用户名和密码写到本地文件中

代码演示:

public static void main(String[] args) {Scanner sc = new Scanner(System.in);FileWriter fw = null;try {fw = new FileWriter("E:/file/userPass.txt");System.out.println("请输入用户名:");String username = sc.next();//灰灰System.out.println("请输入密码:");String password = sc.next();fw.write("username:" + username);fw.write("\r\n");fw.write("password:" + password);} catch (IOException e) {e.printStackTrace();} finally {try {fw.close();} catch (IOException e) {e.printStackTrace();}System.out.println("存储成功!");}}

3.12 字符缓冲流

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

3.13 字符缓冲流特有功能

BufferedWriter:
void newLine​():写一行行分隔符,行分隔符字符串由系统属性定义

BufferedReader:
public String readLine​() :读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为null

public static void main(String[] args) throws IOException {BufferedReader br = null;BufferedWriter bw = null;try {br = new BufferedReader(new FileReader("E:/file/userPass.txt"));bw = new BufferedWriter(new FileWriter("src/com/itgaohe/123.txt"));String line = null;while ((line = br.readLine()) != null) {bw.write(line);bw.newLine();}} catch (Exception e) {e.printStackTrace();} finally {br.close();bw.close();}
}

3.14 案例:读取文件中的数据排序后再次写到本地

需求:读取文件中的数据,排序后再次写到本地文件
步骤:
读取数据
将数据排序
写回本地

代码展示:

userPass

username:huihui
password:123
public static void main(String[] args) {BufferedReader br = null;BufferedWriter bw = null;ArrayList<String> list = new ArrayList<>();try {br = new BufferedReader(new FileReader("E:/file/userPass.txt"));bw = new BufferedWriter(new FileWriter("src/com/itgaohe/123.txt"));String line = null;while ((line = br.readLine()) != null) {list.add(line);}//倒序排序Collections.sort(list, (o1, o2) -> o2.hashCode() - o1.hashCode());for (String s : list) {bw.write(s);bw.newLine();}} catch (Exception e) {e.printStackTrace();} finally {try {br.close();} catch (IOException e) {e.printStackTrace();}try {bw.close();} catch (IOException e) {e.printStackTrace();}}
}

3.15 案例:使用字符缓冲流读取user.txt中用户名和密码

需求:读取用户名和密码,判断用户输入数据是否正确
步骤:
用户通过控制台输入数据
读取文件内容,进行数据撕裂获取username, password
比较用户输入数据是否正确

代码展示:

 public static void main(String[] args) {BufferedReader br = null;ArrayList<String> list = new ArrayList<>();String username = null;String password = null;int time = 3;Scanner sc = new Scanner(System.in);try {br = new BufferedReader(new FileReader("E:/file/userPass.txt"));String line = null;while ((line = br.readLine())!=null){list.add(line);}for (String s : list) {if (s.startsWith("username")){//说明是账号String[] split = s.split(":");username = split[1];System.out.println(username);}if (s.startsWith("password")){String[] split = s.split(":");password = split[1];System.out.println(password);}}//输入值进行比较while (true){System.out.println("请输入用户名:");String username1 = sc.next();System.out.println("请输入密码:");String password1 = sc.next();if (username1.equals(username)&&password1.equals(password)){System.out.println("登陆成功!!!");break;}else {System.out.println("比对失败!!!请重新输入:你还有" +(--time)+"次机会!");if (time <=0){System.out.println("你账号被锁定!");break;}}}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {try {br.close();} catch (IOException e) {e.printStackTrace();}}}

3.16 IO流小结

在这里插入图片描述

  1. 学会字节流完成文件的读取操作!!!
  2. 学会用缓冲流完成文件读取操作!!!
  3. 字节流打印中文为什么乱码?字节流复制文件为什么不会乱码?
  4. 字符串转字节 、 字节转字符串!!!
  5. 转码的概念。什么情况下会乱码!!!

能力目标

  1. 把一个照片文件用 IO流(以字节为单位去搬运) 拷贝到项目模块目录下。

  2. 把一个照片文件用 IO流(以数组为单位去搬运) 拷贝到项目模块目录下。

  3. 把一个照片文件用 缓冲流 拷贝到项目模块目录下。

  4. 把一个文件夹目录 拷贝到项目模块目录下。(要求:文件夹及其子文件全部拷贝)

    尝试使用缓冲流完成这个copy操作。

4.转换流

4.1 案例引入

用转换流往a.txt数据文件写入GBK编码中文数据,再用转换流读取a.txt,解码为GBK编码

输入流----读取文件

输出流----将内容输入到文件

代码展示:

public static void main(String[] args) throws IOException {File file1 = new File("E:\\file\\aa\\image\\a.txt");File file2 = new File("src/com/itgaohe/123.txt");OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file2), "GBK");InputStreamReader isr = new InputStreamReader(new FileInputStream(file1), "GBK");int len = -1;while ((len = isr.read()) != -1) {osw.write(len);}osw.close();isr.close();}

4.2 查看本地文件的编码格式

那我们如何才能知道本地文件的编码格式是什么样的呢?

在这里插入图片描述

4.3 查看idea中的编码格式

在我们查看输入到idea中 如果代码出现乱码的问题 该怎么解决?

在这里插入图片描述

查看idea默认的项目编码格式 我们发现 项目指定的编码格式是UTF-8 而我们展出

在这里插入图片描述

4.4 转换流模型图

在这里插入图片描述

在这里插入图片描述

4.5 转换流读写数据

构造方法

IDEA中默认字符编码格式为:UTF-8

方法名说明
InputStreamReader(InputStream in)使用默认字符编码创建InputStreamReader对象
InputStreamReader(InputStream in,String chatset)使用指定的字符编码创建InputStreamReader对象
OutputStreamWriter(OutputStream out)使用默认字符编码创建OutputStreamWriter对象
OutputStreamWriter(OutputStream out, String charset)使用指定的字符编码创建OutputStreamWriter对象

代码展示:

 public static void main(String[] args) throws IOException {File file1 = new File("E:\\file\\aa\\image\\a.txt");File file2 = new File("src/com/itgaohe/123.txt");
//        InputStreamReader isr = new InputStreamReader(new FileInputStream(file1));//utf-8InputStreamReader isr = new InputStreamReader(new FileInputStream(file1), "GBK");
//        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file2));//UTF-8OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file2),"GBK");int len = -1;while ((len = isr.read()) != -1) {osw.write(len);}osw.close();isr.close();}

4.6 转换流的使用场景

在JDK11之前,指定编码读写

JDK11之后,

FileReader(File file, Charset charset):创建一个新的FileReader,给出File读取和创建charset
FileReader(String fileName, Charset charset):创建一个给定文件名称的FileReader,给出File读取和创建charset

【注意】:
charset需要通过Charset.forName(“”)获取

4.7 小结

转换流就是来进行字节流和字符流之间转换的

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

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

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

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

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

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

5.对象操作流

5.1 案例引入

案例:用对象操作流读写多个对象

需求:创建多个Javabean类对象到文件中,再次读取到内存。

思路:
创建学生对象
利用对象操作输出流写到本地
利用对象操作输入流读到内存

代码展示:

/***  read():*      读取到文件末尾返回值是 -1*  readLine():*      读取到文件的末尾返回值 null*  readObject():*      读取到文件的末尾 直接抛出异常java.io.EOFException*  如果要序列化的对象有多个,不建议直接将多个对象序列化到文件中,因为反序列化时容易出异常*      建议: 将要序列化的多个对象存储到集合中,然后将集合序列化到文件中*/
public class ObjectStream2 {public static void main(String[] args) throws Exception {// 序列化//1.创建序列化流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src\\com\\itgaohe\\123.txt"));ArrayList<Student> arrayList = new ArrayList<>();//2.创建多个学生对象Student s01 = new Student("佟丽娅",30,"女");Student s02 = new Student("王宝强",30,"男");//3.将学生对象添加到集合中arrayList.add(s01);arrayList.add(s02);//4.将集合对象序列化到文件中oos.writeObject(arrayList);oos.close();// 反序列化//5.创建反序列化流对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src\\com\\itgaohe\\123.txt"));//6.将文件中的对象数据,读取到内存中Object obj = ois.readObject();ArrayList<Student> list = (ArrayList<Student>)obj;ois.close();for (Student s : list) {System.out.println(s.getName() + "," + s.getAge());}}
}

5.2 对象操作流的特点

可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中。

5.3 了解对象操作流

在这里插入图片描述

5.4 对象操作流模型图

在这里插入图片描述

5.5 对象操作流概述

对象操作流分为两类:对象操作输入流和对象操作输出流
对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象

5.6 对象序列化反序列化概述

什么是对象的序列化和反序列化?

对象序列化介绍

  • 对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
  • 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
  • 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
  • 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化

对象序列化流: ObjectOutputStream

  • 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象 。
  • 只有支持java.io.Serializable接口的对象才能写入流。
  • writeObject方法用于将对象写入流。

5.7 对象操作输出流

对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
构造方法

方法名说明
ObjectOutputStream(OutputStream out)创建一个写入指定的OutputStream的ObjectOutputStream

序列化对象的方法

方法名说明
void writeObject(Object obj)将指定的对象写入ObjectOutputStream

注意事项

  • 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
  • Serializable是一个标记接口,实现该接口,不需要重写任何方法

5.8 对象操作输入流

对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
构造方法

方法名说明
ObjectInputStream(InputStream in)创建从指定的InputStream读取的ObjectInputStream

反序列化对象的方法

方法名说明
Object readObject()从ObjectInputStream读取一个对象

代码展示:

Student

public class Student implements Serializable {private String name;private int age;private String sex;//getset 有参无参 toStirng 实现序列化接口
}

Test

public static void main(String[] args) throws IOException, ClassNotFoundException {//3.创建对象Student student = new Student("杨金辉",18,"男");//1.创建对象字节输出流ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src\\com\\itgaohe\\123.txt"));//2.将对象输出到 文件中  //存储的是对象在txt文件中oos.writeObject(student);//1.创建对象输入流ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src\\com\\itgaohe\\123.txt"));//2.读取txt文件中的对象Object object = ois.readObject();//3.输出对象System.out.println(object.toString());ois.close();oos.close();}

5.9 对象操作流注意事项

注意:

  1. 对象流不仅可以读写对象,还可以读写基本数据类型。
  2. 使用对象流读写对象时,该对象必须序列化与反序列化。
  3. 系统提供的类(如Date等)已经实现了序列化接口,自定义类必须手动实现序列化接口。

代码展示:

public static void main(String[] args) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/com/itgaohe/123.txt"));oos.writeInt(123);oos.writeObject("灰灰真帅!");oos.writeObject(new Date());oos.writeObject(new Student());//java.io.NotSerializableExceptionoos.close();
}

5.10 对象操作流问题

问题引入:

用对象序列化流序列化了一个对象后,我们再去修改对象所属的Javabean类,比如添加一个属性,读取数据会不会出问题呢?

会出问题,会抛出InvalidClassException异常

代码展示–先做如下操作:

1.对学生对象进行序列化存储

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/com/itgaohe/123.txt"));
oos.writeObject(new Student("灰灰",18,"nan"));
oos.close();

2.将 序列化存储隐掉,在学生类中添加一个新的字段 同时打开反序列化存储

public class Student implements Serializable {private String name;private int age;private String sex;private String number;//新加字段...}
public static void main(String[] args) throws IOException, ClassNotFoundException {
//        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/com/itgaohe/123.txt"));
//        oos.writeObject(new Student("灰灰",18,"nan"));//java.io.NotSerializableException
//        oos.close();ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/com/itgaohe/123.txt"));System.out.println(ois.readObject());ois.close();}

3.控制台报错 java.io.InvalidClassException

Exception in thread "main" java.io.InvalidClassException: com.itgaohe.test07.Student; local class incompatible: stream classdesc serialVersionUID = -3749205021108360658, local class serialVersionUID = -1984186709931597183

解决方案:

1.给对象所属的类加一个serialVersionUID

private static final long serialVersionUID = 42L;

2.自动生成
在这里插入图片描述

在这里插入图片描述

如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?

给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

6.Properties

6.1 Properties概述:

是一个Map体系的集合类 继承了Hashtable implements Map接口

Properties中有跟IO相关的方法

作用:只存字符串!!!

6.2 Properties方法

Properties作为集合的特有方法:

方法名说明
Object setProperty(String key, String value)设置集合的键和值,都是String类型,底层调用Hashtable方法 put
String getProperty(String key)使用此属性列表中指定的键搜索属性
SetstringPropertyNames()从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串

6.3练习: Properties作为Map集合的使用(增删改查)

public static void main(String[] args) {//增Properties properties = new Properties();properties.setProperty("大哥","123");System.out.println(properties);//删p.remove("username");//改p.setProperty("username","lisi");//查-System.out.println(p.getProperty("username"));//遍历   -- 获取keysSet<Object> keySet = p.keySet();for (Object key : keySet) {Object value = p.get(key);System.out.println(key + "---" + value);}System.out.println("==========");//stringPropertyNames() 获取keys  string类型Set<String> names = p.stringPropertyNames();for (String key : names) {String property = p.getProperty(key);System.out.println(key+"--"+property);}
}

6.4 Properties和IO流结合的方法

方法名说明
void load(InputStream inStream)从输入字节流读取属性列表(键和元素对),+p集合中存入的内容
void load(Reader reader)从输入字符流读取属性列表(键和元素对)
void store(Writer writer, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合使用load(Reader)方法的格式写入输出字符流

代码展示:

a.properties

jdbc.username=root
jdbc.password=1234
public static void main(String[] args) throws IOException {Properties properties = new Properties();properties.setProperty("gaoheday01", "张飞");properties.setProperty("gaoheday02", "张飞2");properties.setProperty("gaoheday03", "张飞3");
//        FileReader fr = new FileReader("src/com/itgaohe/a.properties");
//        properties.load(fr);
//        properties.store(new FileWriter("src/com/itgaohe/b.properties"),"备注:");InputStream is = new FileInputStream("src/com/itgaohe/a.properties");properties.load(is);properties.store(new FileOutputStream("src/com/itgaohe/b.properties"),"");Set<String> set = properties.stringPropertyNames();for (String s : set) {System.out.println(s+":"+properties.getProperty(s));}}

6.5 案例

案例需求
在Properties文件中手动写上用户名密码,读取到集合中,将该数据封装成用户对象,写到本地文件.

  • 实现步骤
    • 创建Properties集合,将本地文件中的数据加载到集合中.
    • 获取集合中的键值对数据,封装到用户对象中.
    • 创建序列化流对象,将用户对象序列化到本地文件中.

代码演示:

a.properties

jdbc.username=root
jdbc.password=1234

test

public static void main(String[] args) throws IOException {//1.创建Properties集合 将本地文件中的数据加载到集合中Properties prop = new Properties();//2.创建字符输入流 读取配置文件中的信息FileReader fr = new FileReader("src/com/itgaohe/a.properties");//3.用集合对象 加载 配置文件prop.load(fr);fr.close();//4.获取Properties集合中的键值对数据 封装到对象中String username = prop.getProperty("jdbc.username");String password = prop.getProperty("jdbc.password");User user = new User(username, Integer.parseInt(password));//3.创建序列化输出流 输出到本地中ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/com/itgaohe/123.txt"));oos.writeObject(user);oos.close();
}

etProperty(“gaoheday03”, “张飞3”);
// FileReader fr = new FileReader(“src/com/itgaohe/a.properties”);
// properties.load(fr);
// properties.store(new FileWriter(“src/com/itgaohe/b.properties”),“备注:”);

    InputStream is = new FileInputStream("src/com/itgaohe/a.properties");properties.load(is);properties.store(new FileOutputStream("src/com/itgaohe/b.properties"),"");Set<String> set = properties.stringPropertyNames();for (String s : set) {System.out.println(s+":"+properties.getProperty(s));}
}

## 6.5  案例**案例需求**
在Properties文件中手动写上用户名密码,读取到集合中,将该数据封装成用户对象,写到本地文件.- 实现步骤- 创建Properties集合,将本地文件中的数据加载到集合中.- 获取集合中的键值对数据,封装到用户对象中.- 创建序列化流对象,将用户对象序列化到本地文件中.**代码演示:****a.properties**```java
jdbc.username=root
jdbc.password=1234

test

public static void main(String[] args) throws IOException {//1.创建Properties集合 将本地文件中的数据加载到集合中Properties prop = new Properties();//2.创建字符输入流 读取配置文件中的信息FileReader fr = new FileReader("src/com/itgaohe/a.properties");//3.用集合对象 加载 配置文件prop.load(fr);fr.close();//4.获取Properties集合中的键值对数据 封装到对象中String username = prop.getProperty("jdbc.username");String password = prop.getProperty("jdbc.password");User user = new User(username, Integer.parseInt(password));//3.创建序列化输出流 输出到本地中ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/com/itgaohe/123.txt"));oos.writeObject(user);oos.close();
}

这篇关于day11-IO流的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/983269

相关文章

Java IO 操作——个人理解

之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理( 原文章),记录一下。 首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。          数据来源的操作: 来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。    File file = new File("path");   字

springboot体会BIO(阻塞式IO)

使用springboot体会阻塞式IO 大致的思路为: 创建一个socket服务端,监听socket通道,并打印出socket通道中的内容。 创建两个socket客户端,向socket服务端写入消息。 1.创建服务端 public class RedisServer {public static void main(String[] args) throws IOException {

Java基础回顾系列-第七天-高级编程之IO

Java基础回顾系列-第七天-高级编程之IO 文件操作字节流与字符流OutputStream字节输出流FileOutputStream InputStream字节输入流FileInputStream Writer字符输出流FileWriter Reader字符输入流字节流与字符流的区别转换流InputStreamReaderOutputStreamWriter 文件复制 字符编码内存操作流(

android java.io.IOException: open failed: ENOENT (No such file or directory)-api23+权限受权

问题描述 在安卓上,清单明明已经受权了读写文件权限,但偏偏就是创建不了目录和文件 调用mkdirs()总是返回false. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_E

JavaEE-文件操作与IO

目录 1,两种路径 二,两种文件 三,文件的操作/File类: 1)文件系统操作 File类 2)文件内容操作(读文件,写文件) (1)打开文件 (2)关闭文件 (3)读文件/InputStream (4)写文件/OutputStream (5)读文件/reader (6)写文件/writer (7)Scanner 四,练习: 1,两种路径 1)绝对路径

Python---文件IO流及对象序列化

文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 前言 前文模块中提到加密模块,本文将终点介绍加密模块和文件流。 一、文件流和IO流概述         在Python中,IO流是用于输入和输出数据的通道。它可以用于读取输入数据或将数据写入输出目标。IO流可以是标准输入/输出流(stdin和stdout),也可以是文件流,网络流等。

标准IO与系统IO

概念区别 标准IO:(libc提供) fopen fread fwrite 系统IO:(linux系统提供) open read write 操作效率 因为内存与磁盘的执行效率不同 系统IO: 把数据从内存直接写到磁盘上 标准IO: 数据写到缓存,再刷写到磁盘上

linux基础IO——动静态库——进程编址、进程执行、动态库加载

前言:本节内容为基础IO部分的最后一节, 主要是为了讲一下动静态库里面的动态库如何加载到内存, 动态库的地址等等。 但是,这些内容牵扯到了程序的编址, 程序的加载, 进程的执行等等知识点, 所以,我们会从程序的编址讲起, 一直到进程的执行, 以及动态库加载结束。         ps:本节内容涉及到了进程地址空间, 磁盘的内容, 建议友友们了解相关知识后再来观看。 目录

mybatis错误——java.io.IOException Could not find resource comxxxxxxMapper.xml

在学习Mybatis的时候,参考网上的教程进行简单demo的搭建,配置的没有问题,然后出现了下面的错误! Exception in thread "main" java.lang.RuntimeException: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause:

day09-IO-字符流其它流

一、字符流 字符流(只能做文本文件的处理)字符输入流 Reader--FileReader字符输出流 Writer--FileWriter​使用文件字符输入流的好处:读取中文不会出现乱码问题 1.1 字符输入流 构造器说明public FileReader (File file)创建字符输入流管道与源文件接通public FileReader(String pathname)创建字