【Java ee 初阶】文件IO和操作(下)
书接上文
文本操作的方法
String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象表示 |
此处是针对File对象打印得到的效果(调用了File的toString)
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
前者只能创建一层目录,后者可以一次性创建多层目录
boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操作 |
不光能重命名,还能够移动位置
文件内容操作
读文件:硬盘的数据读到内存中去
写文件:把内存的数据写到硬盘里
我们通过流对象进行文件内容操作
Java通过一系列的类来表示“流对象”,一般分成两个大类
1.字节流:读写数据的时候,以字节为基本单位,一次最少读写一个字节。读写二进制文件的是偶,通常使用字节流
InputStream 读
OutputStream 写
2.字符流:读写数据的时候,以字符为基本单位,一次最少读写一个字符(一个字符可能对应多个字节),看编码方式。读写文本文件,通常使用字符流。
Reader 读
Writer 写
在IO操作过程中,输入输出指的是以CPU为基准来进行考虑的,从硬盘读取内容到内存中去,再从内存中读取内容放到CPU中。
*C语言中的“字符流”本质上是“字节流”,C语言没有“Java的字符的概念”,本身没有处理UTF-8,GBK等编码的能力,压根处理不了中文。C++也一样。
是一个抽象类,因此不能直接创建实例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;public class demo6 {public static void main(String[] args) throws Exception {// 创建文件,这个过程,对应到操作系统中,称为“打开文件”InputStream inputStream = new FileInputStream("d:/test.txt");while(true){int c = inputStream.read(); // 读取一个字节,对应到操作系统中,称为“读取文件”if(c == -1){ // 读取到文件末尾,返回 -1break; // 跳出循环}System.out.printf("0x%x\n",c);}}}
对照ASCII表:ASCII码对照表,ASCII码一览表(非常详细) - C语言中文网
操作系统中,流这个概念,不仅仅是能够搭配文件使用的,还有其他的用途(例如搭配网络进行使用)
站在操作系统的视角,如果想要读写文件,需要先打开文件。
无参数版本,一次只能读取一个字节
一个参数版本,尝试把这个参数中的字节数组给填满
三个参数版本,也是尝试把数据放到b字节数组中,而不是从头开始放,是从offset的下标开始最多填充len个,只使用了数组的一部分
若test里面存放的是“你好”
第二种写法:
此处的buffer参数,称为“输出型参数”。实现构造好一个空的数组,不是null,而是里面的元素全为0的数组,由read方法内部,往参数数组中进行填充。n表示实际读到的有效字节数。
一个方法可以看成是一个“工厂”,参数是“原材料”,返回值就是“产品”。
Java中一个方法,只能返回一个值,如果需要返回多个值,要么借助输出型参数,要么把返回的多个值打包成一个类。一个方法返回多个值,很多语言多支持,Pyhthon,Go......这种语法就完全不需要输出型参数。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;public class demo7 {public static void main(String[] args) throws IOException {InputStream file = new FileInputStream("d:/test.txt");byte[] buffer = new byte[1024]; // 缓冲区,用于存储读取到的字节while (true) {int n = file.read(buffer);if (n == -1) { // 读取到文件末尾,返回 -1break; // 跳出循环}for (int i = 0; i < n; i++) { // 循环 n 次,输出缓冲区中的字节System.out.printf("0x%x\n", buffer[i]); // 输出字节的十六进制表示}}}
}
内存泄漏
内存泄漏问题:内存申请了之后,没有释放,可用内存越来越少,后序内存都申请不了了。对于Java来说,JVM存在垃圾回收机制,能够自动的把不适用更大内存全部释放掉。
文件资源,也会涉及到“泄漏”
进程PCB中有一个属性,文件描述符表(这是一个顺序表),每次打开一个文件,就会再找个表里插入一个元素。问年间描述符表,长度是定长的(无法自动扩容)。光打开文件,不关闭文件,此时就会逐渐把找个表里的内容消耗殆尽,后序再尝试打开,就会打开失败。
进行修复之后
此时代码并不美观
继续修改得到:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;public class demo8 {public static void readFile() throws IOException{//利用try-with-resources自动关闭流try(InputStream inputStream = new FileInputStream("d:/test.txt")){byte[] buffer = new byte[1024]; // 缓冲区,用于存储读取到的字节while (true) {int n = inputStream.read(buffer); // 读取文件内容到缓冲区if (n == -1) { // 读取到文件末尾,返回 -1break; // 跳出循环} for (int i = 0; i < n; i++) { // 循环 n 次,输出缓冲区中的字节System.out.printf("0x%x\n", buffer[i]); // 输出字节的十六进制表示}}}catch(IOException e) {e.printStackTrace(); // 打印异常信息}
写文件OutputStream
写一个字节
写一个字节数组
写字节数组的一个部分
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;public class demo9 {public static void writeFile() {try (OutputStream os = new FileOutputStream("d:/test.txt")) {os.write(97);os.write(98);os.write(99);os.write(100);} catch (IOException e) {e.printStackTrace(); // TODO: handle exception}// TODO Auto-generated method stub} public static void main(String[] args) {writeFile();}}
每一次写,都将原来的文本内容清空重新开始写
加上append这个参数,表示“追加写”,不会清空原有内容,新写入的内容就会从文件末尾继续追加。
文件读取
一次性只读取一个字符,-1表示结束
一次性读取若干个字符,填充到char[],返回实际读到的字符个数
一次性读取若干个字符,填充到 char[] 的一部分
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;public class demo10 {public static void main(String[] args) throws FileNotFoundException {try(Reader reader = new FileReader("d:/test.txt")){while(true){int ch = reader.read();if(ch == -1){break;}char c = (char)ch; // 强制类型转换,将 int 类型转换为 char 类型System.out.print(c); // 输出字符}} catch (IOException e) {e.printStackTrace(); // 打印异常栈信息,方便调试 // TODO: handle exception}}}
write写文件操作
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;public class demo11 {public static void main(String[] args) {try(Writer writer = new FileWriter("d:/test.txt",true)){ // 追加模式,true 表示追加到文件末尾,false 表示覆盖原文件内容writer.write("Hello, World!"); // 写入字符串到文件} catch (IOException e) {e.printStackTrace();// TODO: handle exception}}}
三个应用题
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
import java.io.File;
import java.util.Scanner;public class demo12 {public static void main(String[] args) {//1.让用户输入要查询的文件名Scanner scanner = new Scanner(System.in);System.out.println("请输入要搜索的目录:");String dirPath = scanner.next();System.out.println("请输入要查询的文件名:");String fileName = scanner.next();//2.判定目录是否存在File dir = new File(dirPath);if(!dir.isDirectory()){System.out.println("目录不存在");return; // 终止程序}//3.进行搜索,递归的遍历目录中所有的文件和子目录searchFile(dir,fileName); // 调用递归方法进行搜索}private static void searchFile(File dir, String fileName) {// 1.获取目录中的所有文件和子目录File[] files = dir.listFiles(); // 获取目录中的所有文件和子目录if(files == null){ // 如果目录为空,直接返回return; // 终止递归}//2.遍历files数组,判定每个元素的类型for(File file : files){ // 遍历files数组,判定每个元素的类型if(file.isDirectory()){ // 如果是目录,递归调用searchFile方法searchFile(file,fileName); // 递归调用searchFile方法}else{ // 如果是文件,判断文件名是否包含要查询的字符串if(file.getName().contains(fileName)){ //如果找到了,那么就要尝试删除trydelete(file); // 调用trydelete方法进行删除}}}}private static void trydelete(File file) { System.out.println("准备删除文件:"+file.getAbsolutePath());Scanner scanner = new Scanner(System.in); // 创建Scanner对象,用于读取用户输入System.out.println("是否要删除文件:" + file.getAbsolutePath() + "?(y/n)"); // 打印提示信息String choice = scanner.next(); // 读取用户输入的字符串if(choice.equals("y")){file.delete();System.out.println("文件删除成功");}
}}
进行普通文件的复制
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Scanner;public class demo13 {//进行普通文件的复制public static void main(String[] args) throws Exception {//1.输入源文件目录和目标文件目录Scanner scanner = new Scanner(System.in);System.out.println("请输入源文件目录:");String src = scanner.next();System.out.println("请输入目标文件目录:");String dest = scanner.next();//2.判定源文件是否存在File srcFile = new File(src);if(!srcFile.exists()){System.out.println("源文件不存在");return; //结束程序}//3.判定目标文件的父目录是否存在,如果不存在,则创建File destFile = new File(dest);if(!destFile.getParentFile().exists()){ //如果目标文件的父目录不存在,则创建return;}//4.进行文件的复制copy(srcFile,destFile);}//进行文件的复制public static void copy(File srcFile,File destFile) throws Exception {try(InputStream inputStream = new FileInputStream(srcFile); OutputStream outputStream =new FileOutputStream(destFile)) {while(true){ //循环读取文件内容,直到文件末尾byte[] buffer = new byte[1024]; //创建一个缓冲区,用于存储读取到的文件内容int len = inputStream.read(buffer); //读取文件内容到缓冲区中,返回读取到的字节数if(len == -1){ //如果读取到的字节数为-1,则表示文件末尾break; //结束循环}outputStream.write(buffer,0,len); //将读取到的文件内容写入到目标文件中,从缓冲区的0位置开始,写入len个字节} } catch (IOException e) {e.printStackTrace(); // TODO: handle exception}}}