JAVA进阶 (IO流)—— 基本流

接下来的两篇文章我们将系统化的详细学习IO流的内容:

基本流 JAVA进阶(IO流) —— 基本流
高级流 JAVA进阶(IO流) —— 高级流

目录

一、前言

二、IO流的分类

三、 字节流

1. 输出流  FileOutputStream

 1.1 FileOutputStream 写数据的三种方式

1.2 FileOutputStream 写数据的两个小问题

2.  输入流  FileInputStream

2.1 FileInputStream 循环读取

2.2 FileInputStream 读数据的两种方式

3. 练习:文件拷贝

四、字符流

 1. 字符输入流 FileReader 

2. 字符输出流 FileWriter

3. 字符流原理分析

五、综合练习

1. 拷贝

2. 文件加密

3. 修改文件中的数据


一、前言

File:表示系统中的文件或者文件夹的路径。(详细见:JAVA进阶 —— File

 注意: File类只能对文件本身进行操作,不能对写文件里面存储的数据

IO流:用来读写文件中的数据(可以读写文件、或者网络中的数据 . . . )

二、IO流的分类

流的方向 操作文件类型

 纯文本文件:Windows自带的记事本打开能读懂的文件。

三、 字节流

 注意:字节流读取文件的时候,文件中不要有中文。

1. 输出流  FileOutputStream

操作本地文件的字节输出流,可以把程序中的数据写到本地文件中。

书写步骤:

① 创建字节输出流对象

  • 细节1:参数是字符串表示的路径或者File对象都是可以的。
  • 细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的。
  • 细节3:如果文件已经存在,则会清空文件

② 写数据

  • 细节:write方法的参数是整数,但是实际上写到本地文件中的是整数ASCII上对应的字符

③ 释放资源

  • 细节:每次使用完流之后都要释放资源。
public class ByteStreamDemo1 {
	public static void main(String[] args) throws IOException {
		// 需求: 写一段文字到本地文件中

		// 1.创建对象
		// 写出 -> 输出流 OutputStream

		// 编译时期异常 throws FileNotFoundException
		FileOutputStream fos = new FileOutputStream("java02\\a.txt");
		// 2.写出数据
		fos.write(97);
		// 3.释放资源
		fos.close();
	}
}

 1.1 FileOutputStream 写数据的三种方式

方法名称 说明
void write ( int  b ) 一次写一个字节数据
void write ( byte [ ]  b ) 一次写一个字节数组数据
void write ( byte [ ] b , int off, int len ) 一次写一个字节数组的部分数据
public class ByteStreamDemo2 {
	public static void main(String[] args) throws IOException {
		// 1.创建对象
		FileOutputStream fos = new FileOutputStream("java02\\a.txt");
		// 2.写出数据
		// ①、一次写一个字符数据
		fos.write(97); // a
		fos.write(98); // b

		// ②、一次写入一个字符数组
		byte[] bytes = { 97, 98, 99, 100, 101 };
		fos.write(bytes);

		// ③、一次写一个字符数组的部分数据
		// 参数一:数组 ;参数二:起始索引 ; 参数三:个数
		fos.write(bytes, 1, 2); // b c

		// 3.释放资源
		fos.close();
	}
}

1.2 FileOutputStream 写数据的两个小问题

  • 换行写:换行符 \r \n
  • 续写:FileOutputStream("路径", 参数);
  • public class ByteStreamDemo3 {
    	public static void main(String[] args) throws IOException {
    		// 1.创建对象
    		FileOutputStream fos = new FileOutputStream("java02\\a.txt", true);
    		// 2.写出数据
    		String str = "abcdefghijklmn";
    		byte[] bytes = str.getBytes(); // 字符串转换成字节数组
    		fos.write(bytes);
    
    		// 3.换行
    		// 换行符
    		// windows:00\r\n Linux: \n Mac:\r
    		String str3 = "\r\n";
    		byte[] bytes3 = str3.getBytes();
    		fos.write(bytes3);
    
    		String str2 = "666";
    		byte[] bytes2 = str2.getBytes();
    		fos.write(bytes2); // 此时一开始并没有换行 需要上写一个换行符
    
    		// 4.续写
    		// 如果想要续写,打开续写开关即可
    		// 打开位置,创建对象的第二个参数
    		// 默认false:表示关闭,此时创建对象会清空文件
    		// 手动true:表示打开续写,此时创建对象不会清空文件
    		// new FileOutputStream("java02\\a.txt",true);
    
    		// 5.释放资源
    		fos.close();
    	}
    }

2.  输入流  FileInputStream

  • 操作本地文件的字符输入流,可以把本地文件中的数据读取到程序当中。

书写步骤:

① 创建字节输入流对象

  • 细节:如果文件不存在,就直接报错。

② 读数据

  • 细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字。
  • 细节2:读取到文件末尾时,read方法返回 -1。

③ 释放资源

  • 细节:每次使用完流必须要释放资源。

public class ByteStreamDemo4 {
	public static void main(String[] args) throws IOException {
		// 1.创建对象
		FileInputStream fis = new FileInputStream("java02\\a.txt");
		// a.txt : abcde
		// 2.读取数据
		int b1 = fis.read();
		System.out.println(b1); // 97
		System.out.println((char) b1); // 强转: a
		// 读取不到就会返回 -1
		// 3.释放资源
		fis.close();
	}
}

2.1 FileInputStream 循环读取

read方法:读取数据,而且是读取一个数据移动一次指针

public class ByteStreamDemo5 {
	public static void main(String[] args) throws IOException {
		// 1.创建对象
		FileInputStream fis = new FileInputStream("java02\\a.txt");
		// a.txt : abcde

		// 2.循环读取
		//定义第三方变量
		int b;
		while ((b = fis.read()) != -1) {
			System.out.println((char) b);
		}

		// 以下读取方式是错误的
		//read方法:读取数据,而且是读取一个数据移动一次指针
		//相当于迭代器的next方法
		// while (fis.read() != -1) {
		// System.out.println(fis.read()); // 98 100 -1
		// }

		// 释放资源
		fis.close();
	}
}

2.2 FileInputStream 读数据的两种方式

方法名称 说明
public int read ( ) 一次读一个字节数据
public int read ( byte [ ] buffer ) 一次读一个字节数组数据

 注意:一次读一个字节数组的数据,每次读取会尽可能把数组填满。

public class ByteStreamDemo6 {
	public static void main(String[] args) throws IOException {
		// 1.创建对象
		FileInputStream fis = new FileInputStream("java02\\a.txt");
		

		// 2.读取数据
		byte[] bytes = new byte[2];
		//一次读取多个字节数据:具体读多少,跟数组的长度有关
		//返回值:本次读取到了多少个字节数据
		int len = fis.read(bytes);
		System.out.println(len);
		String str= new String(bytes);
		System.out.println(str);
		
		//3.释放资源
		fis.close();
	}
}

3. 练习:文件拷贝

需求:把D: \aaa\movie . mp4拷贝到当前模块下。
注意:选择一个比较小的文件.不要太大。

public class ByteStreamDemo6 {
	public static void main(String[] args) throws IOException {
		// 1.创建对象
		FileInputStream fis = new FileInputStream("D:\\aaa\\movie.mp4");
		FileOutputStream fos = new FileOutputStream("java02\\copy.mp4");

		// 2.拷贝
		// 需要边读边写
		int b;
		while ((b = fis.read()) != -1) {
			fos.write(b);
		}
		// 3.释放资源
		// 规则: 先开的最后关闭
		fos.close();
		fis.close();
	}
}

 弊端: FileInputStream 一次读写一个字节,速度慢。

 解决方案:FileInputStream 使用 byte[] 数组一次遍历多个数据。

public class ByteStreamDemo6 {
	public static void main(String[] args) throws IOException {
		long start = System.currentTimeMillis();
		
		// 1.创建对象
		FileInputStream fis = new FileInputStream("D:\\aaa\\movie.mp4");
		FileOutputStream fos = new FileOutputStream("java02\\copy.mp4");
		// 2.拷贝
		int len;
		byte[] bytes = new byte[1024 * 1024 * 5]; // 5兆大小
		while ((len = fis.read(bytes)) != -1) {
			fos.write(len);
		}
		// 3.释放资源
		fos.close();
		fis.close();
		
		long end = System.currentTimeMillis();
		//运行时间
		System.out.println(end - start);
	}
}

四、字符流

字符流的底层其实就是字节流。

特点:

  • 输入流:一次读取一个字节,遇到中文时,一次读多个字节再写到文件中。
  • 输出流:底层会把数据按照指定的编码方式,变成字节再写到文件中。

使用场景: 对于纯文本文件进行读写操作。

 1. 字符输入流 FileReader 

书写步骤:

① 创建字符输入流对象

构造方法 说明
public FileReader ( File file ) 创建字符输入流关联本地文件
public FileReader ( String pathname ) 创建字符输入流关联本地文件
  • 细节:如果文件不存在,就直接报错。

② 读取数据

成员方法 说明
public int read ( ) 读取数据,读到末尾返回 -1
public int read ( char [ ]  buffer ) 读取多个数据,读到末尾返回 -1
  • 细节1:按字节进行读取,遇到中文,一次读多个字节,读取后解码,返回一个整数。
  • 细节2:读到文件末尾时,read方法返回 -1。

③ 释放资源

成员方法 说明
public int close ( ) 释放资源 / 关流
public class CharStreamDemo1 {
	public static void main(String[] args) throws IOException {
		// 1.创建对象并关联本地文件
		FileReader fr = new FileReader("java02\\a.txt");
		// 2.读取数据 
		// 细节1:字符流的底层就是字节流
		// 默认一个字节一个字节的读取的
		// 如果遇到中文,就会一次读取多个字节,GBK一次两个字节 UTF-8一次三个字节

		// 细节2:读取之后,方法底层会进行解码并转换成十进制
		// 十进制作为返回值 并作为字符集上的数字
		
		// 细节3:想要看中文 可以对十进制进行强转
		
		//空参read
		int ch;
		while ((ch - fr.read()) != -1) {
			// System.out.println(ch);
			System.out.println((char) ch);
		}
		
		//带参read:读取数据、解码、强转三者合并,把强转之后字符放进数组
		//空参的read + 强转类型转换
		char[] chars = new char[2];
		int len;
		while((len = fr.read(chars)) != -1) {
			//把数组中的数据变成字符串再进行打印
			System.out.println(new String(chars,0,len));
		}
		// 3.释放资源
		fr.close();
	}
}

2. 字符输出流 FileWriter

书写步骤:

① 创建字符输出流对象

构造方法 说明
public Filewriter ( File file ) 创建字符输出流关联本地文件
public Filewriter ( String pathname ) 创建字符输出流关联本地文件
public Filewriter ( File file , Boolean append ) 创建字符输出流关联本地文件,续写
public Filewriter ( String pathname , Boolean append ) 创建字符输出流关联本地文件,续写
  • 细节1:参数是字符串表示的路径或者File对象都是可以的。
  • 细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
  • 细节3:如果文件已经存在,则会清空文件,如果不想清空可以打开续写开关。

② 写数据

成员方法 说明
void write ( int c ) 写出一个字符
void write ( String str ) 写出一个字符串
void write( String str, int off,int len ) 写出一个字符串的一部分
void write( char [ ] cbuf) 写出一个字符数组
void write( char [ ] cbuf,int off, int len) 写出字符数组的一部分
  • 细节:如果write方法的参数是整数,但是实际上写到本地文件中的是整数在字符集上对应的字符。

③ 释放资源

  • 细节:每次使用完流之后都要释放资源
public class CharStreamDemo2 {
	public static void main(String[] args) throws IOException {
		// 1.创建对象 续写开关打开
		FileWriter fw = new FileWriter("java02\\a.txt", true);
		// 2.写数据
		fw.write(25105); // 写一个字符
		fw.write("你好?"); // 写一个字符串 9个字节
		char[] chars = { 'a', 'b', 'c', '我' };
		fw.write(chars); // 写一个字符数组
		// 3.释放资源
		fw.close();
	}
}

3. 字符流原理分析

① 创建字符输入流对象

  • 底层:关联文件,并创建缓冲区(长度为8192的字节数组)

② 读取数据

  • 底层:
  • 1. 判断缓冲区中是否有数据可以读取
  • 2. 缓冲区没有数据:就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区;如果文件中也没用数据时,返回 -1。
  • 3. 缓冲区有数据:就从缓冲区中读取。  空参的read方法:一次读取一个字节,遇到中文一次读多个字节,把字节解码并转成十进制返回。  有参的read方法:把读取字节、解码、强转三步合并,强转之后的字符放到数组当中。

五、综合练习

1. 拷贝

需求:拷贝一个文件夹,考虑子文件夹。

public class Test01 {
	public static void main(String[] args) {
		// 1.创建对象表示数据源
		File src = new File("D:\\aaa\\src");
		// 2.创建对象表示目的地
		File dest = new File("D:\\aaa\\dest");

		// 3.调用方法开始拷贝
		copydir(src, dest);
	}

	public static void copydir(File src, File dest) throws IOException {
		// 判断目的文件夹是否存在
		dest.mkdir();

		// 递归
		// 1.进入数据源
		File[] files = src.listFiles();
		// 2.遍历数组
		for (File file : files) {
			if (file.isFile()) {
				// 是文件 开始拷贝
				FileInputStream fis = new FileInputStream(file);
				//dest 是文件夹 不是最终目的地 需要从文件开始到文件结束
 				FileOutputStream fos = new FileOutputStream
                                   (new File(dest, file.getName()));
 				byte[] bytes = new byte[1024];
				int len;
				while ((len = fis.read(bytes)) != -1) {
					fos.write(bytes, 0, len);
				}
				fos.close();
				fis.close();
			} else {
				// 是文件夹 递归
				copydir(file, new File(dest, file.getName()));
			}
		}
	}
}

2. 文件加密

需求:
为了保证文件的安全性,就需要对原始文件进行加密存储,再使用的时候再对其进行解密处理。

加密原理:
               对原始文件中的每一个字节数据进行更改,然后将更改以后的数据存储到新的文件中。
解密原理:
               读取加密之后的文件,按照加密的规则反向操作,变成原始文件。

public class Test2 {
	public static void main(String[] args) throws IOException {
		// ^ 异或:两边相同false 两边不同true
		// 1.创建对象关联原始文件
		FileInputStream fis = new FileInputStream("java02\\girl.jpg");
		// 2.创建对象关联加密文件
		FileOutputStream fos = new FileOutputStream("java02\\ency.jpg");
		// 3.机密过程
		int b;
		while ((b = fis.read()) != -1) {
			fos.write(b ^ 2);
		}
		// 4.释放资源
		fos.close();
		fis.close();

		// 解密过程
		FileInputStream fis = new FileInputStream("java02\\ency.jpg");
		FileOutputStream fos = new FileOutputStream("java02\\redu.jpg");
		int b;
		while ((b = fis.read()) != -1) {
			fos.write(b ^ 2);
		}
		fos.close();
		fis.close();
	}
}

3. 修改文件中的数据

需求:

文本文件中有以下的数据:
             2-1-9-4-7-8
将文件中的数据进行排序,变成以下的数据:
             1-2-4-7-8-9

​
public class Test3 {
	public static void main(String[] args) throws IOException {
		// 1.读取数据
		FileReader fr = new FileReader("java02\\a.txt");
		StringBuilder sb = new StringBuilder();
		int ch;
		while ((ch = fr.read()) != -1) {
			sb.append((char) ch);
		}
		fr.close();
		System.out.println(sb);
		// 2.排序
		String str = sb.toString();
		String[] arrStr = str.split("-");

		// 定义数组用于存储数据进行排序
		ArrayList<Integer> list = new ArrayList<>();
		for (String s : arrStr) {
			int i = Integer.parseInt(s);
			list.add(i);
		}
		System.out.println(list);
		// sort: 默认升序排序
		Collections.sort(list);
		System.out.println(list);

		// 3.写出数据
		FileWriter fw = new FileWriter("java02\\a.txt");
		// 打印结果: 1-2-4-7-8-9 通过索引遍历 -》 普通for循环
		for (int i = 0; i < list.size(); i++) {
			if (i == list.size() - 1) {
				fw.write(list.get(i) + "-");
			} else {
				fw.write(list.get(i));
			}
		}
		fw.close();
	}
}

​

猜你喜欢

转载自blog.csdn.net/hdakj22/article/details/129493191