欢迎关注个人主页:逸狼
创造不易,可以点点赞吗~
如有错误,欢迎指出~
目录
文件内容操作
打开 关闭文件
文件描述符表
字节流
读文件
写文件
字符流
读文件
写文件
Scanner
示例一:通过scanner读取文件中的数字
示例二:扫描指定⽬录
示例三:实现文件复制
文件内容操作
文件内容操作:读文件 和 写文件,操作系统都提供了API,Java也进行了封装
Java实现IO流类 有很多,分为 字节流(二进制)和字符流(文本) 两大类
上面这四个类都是抽象类,Java中还提供了许多类 来实现这四个抽象类
但凡类的名字以Reader Writer结尾的,就是实现了Reader和Writer的字符流对象但凡类的名字以InputStream OutputStream结尾的,就是实现了InputStream和OutputStream的字节流对象在这里谈到的输入输出,都是以cpu视角来说的:
数据远离cpu =>输出数据靠近cpu =>输入打开 关闭文件
示例1
这种写法,虽然能够确保严谨,但是比较麻烦
import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;public class Demo9_IO { public static void main(String[] args) { InputStream inputStream=null; try { inputStream = new FileInputStream("./test.txt");//隐含了打开文件 } catch (FileNotFoundException e) { e.printStackTrace(); }finally{ try { inputStream.close();//关闭文件 } catch (IOException e) { e.printStackTrace(); } } }}
如果不使用close关不上,会发生什么?
打开文件 其实是在该进程的 文件描述符表 中,创建了一个新的表项
文件描述符表
文件描述符表 描述了该进程都需要哪些文件,可以认为是一个数组,数组的每个元素就是一个struct file 对象(Linux内核),每个结构体就描述了对应操作的文件信息,数组下标 就称为"文件描述符"
示例2:这种写法更简单可靠
import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;public class Demo10 { public static <inputStream> void main(String[] args) { try(InputStream inputStream=new FileInputStream("./test.txt")) { } catch (IOException e) { e.printStackTrace(); } }}
这里代码中,不用写close关闭,因为close之后,紧接着就是进程的结束(意味着pcb就整个销毁,pcb上的文件描述符表,整个就释放了)
字节流
读文件
读文件 :数据从硬盘上读取到 内存里
示例1:读取test.txt文件的内容,以16进制方式输出
import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;public class Demo10 { public static void main(String[] args) { try (InputStream inputStream = new FileInputStream("./test.txt")) { while (true) { int b = inputStream.read(); if (b == -1) { // 读取完毕了 break; } // 表示字节, 更习惯使用 十六进制 打印显示. System.out.printf("0x%x\n", b); } } catch (IOException e) { e.printStackTrace(); } }}
示例1代码 要频繁读取多次硬盘,当前硬盘的IO是消耗比较大
示例2:使用buffer(缓冲区,暂时存储某些数据),;将硬盘上的数据先填充到buffer内存的字节数组中,尽可能填满 ,虽然一次读的内容多了,但是仍然比一次读一个字节,分多次读效率要高不少
import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;public class Demo11 { public static void main(String[] args) { try(InputStream inputStream = new FileInputStream("./test.txt")){ while(true){ byte[] buffer =new byte[1024]; //n返回值表示read操作,实际读取到多少个字节 int n=inputStream.read(buffer); if(n==-1){ break; } for (int i = 0; i < n; i++) { System.out.printf("0x%x\n",buffer[i]); } } }catch(IOException e){ e.printStackTrace(); } }}
写文件
写文件和读文件非常类似
一个一个字节写入
try(OutputStream outputStream=new FileOutputStream("./test.txt")){ outputStream.write(0x61); outputStream.write(0x62); outputStream.write(0x62); outputStream.write(0x62); }catch (IOException e){ e.printStackTrace(); }
这里的写操作,会默认把之前的内容清空(只要使用outputStream打开文件,内容就没了),再进行写操作,可以使用"追加写"操作,保持原内容不变,在末尾继续写入内容
InputStream/OutputStream读写数据就是按照字节来操作的,如果要读写字符(中文)的话,就需要程序员手动来区分出哪几个字节是一个字符,在确保把这几个字符作为整体来写入
字符流
为了方便处理字符,引入了字符流
读文件
使用字符流读取数据的过程,Java标准库内部就自动对数据的编码进行了转码
示例1:一个一个字符读入
try(Reader reader= new FileReader("./test.txt")){ while(true){ int c=reader.read(); if(c==-1){ break; } char ch=(char)c; System.out.println(ch); } }catch(IOException e){ e.printStackTrace(); }
示例2:读取一串字符
try (Reader reader = new FileReader("./test.txt")) { char[] buffer = new char[1024]; int n = reader.read(buffer); System.out.println(n); for (int i = 0; i < n; i++) { System.out.println(buffer[i]); } } catch (IOException e) { }
写文件
try(Writer writer=new FileWriter("./test.txt",true)){ writer.write("你好世界"); }catch(IOException e){ e.printStackTrace(); }
Scanner
读取文件还有一个好用的工具类:Scanner
示例一:通过scanner读取文件中的数字
import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.util.Scanner;public class Demo16_scanner { public static void main(String[] args) { try(InputStream inputStream=new FileInputStream("./test.txt")){ Scanner scanner=new Scanner(inputStream); while(scanner.hasNextInt()){ System.out.println(scanner.nextInt()); } }catch(IOException e){ e.printStackTrace(); } }}
示例二:扫描指定⽬录
并找到名称或者内容中包含指定字符的所有普通⽂件(不包含⽬录)
import java.io.File;import java.util.Scanner;public class Demo17 { private static void scan(File currentFile, String key) { if (!currentFile.isDirectory()) { return; } File[] files = currentFile.listFiles(); if (files == null || files.length == 0) { return; } for (File f : files) { if (f.isFile()) { // 针对普通文件进行处理. // 判定文件名是否符合要求并提示用户删除 doDelete(f, key); } else { // 针对目录进行处理 // 继续递归 scan(f, key); } } } private static void doDelete(File f, String key) { if (!f.getName().contains(key)) { // 文件名中不包含指定的关键字. return; } // 提示用户, 是否确认要删除 Scanner scanner = new Scanner(System.in); System.out.println(f.getAbsolutePath() + " 是否确认要删除 Y/n"); String choice = scanner.next(); if (choice.equals("Y") || choice.equals("y")) { f.delete(); } } public static void main(String[] args) { System.out.println("请输入要搜索的路径: "); Scanner scanner = new Scanner(System.in); String rootPath = scanner.next(); File rootFile = new File(rootPath); if (!rootFile.isDirectory()) { System.out.println("输入的路径不存在"); return; } System.out.println("请输入要删除的文件名字的关键字"); String key = scanner.next(); // 进行递归查找 scan(rootFile, key); }}
示例三:实现文件复制
将一个文件里的每个字节都读出来,写入到另一个文件里
import java.io.*;import java.util.Scanner;public class Demo18 { public static void main(String[] args) { //1.输入路径,并做校验 Scanner scanner = new Scanner(System.in); System.out.println("请输入源文件的路径: "); String srcPath=scanner.next(); File srcFile= new File(srcPath); if(!srcFile.isFile()){ System.out.println("源文件路径有误"); return; } System.out.println("请输入目标文件的路径: "); String destPath = scanner.next(); File destFile =new File(destPath); if(!destFile.getParentFile().isDirectory()){ System.out.println("目标文件路径有误"); return; } //2.执行复制操作 try(InputStream inputStream = new FileInputStream(srcFile); OutputStream outputStream = new FileOutputStream(destFile)){ while(true){ byte[] buffer =new byte[1024]; int n=inputStream.read(buffer); System.out.println("n="+n); if(n == -1){ break; } //需要把buffer写入到outputStream中 outputStream.write(buffer,0,n); } }catch(IOException e){ e.printStackTrace(); } }}
示例四
实现递归遍历目录"查找文件",按照文件的内容查询
import java.io.*;import java.util.Scanner;public class Demo19 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("请输入要搜索的路径: "); String rootPath = scanner.next(); File rootFile = new File(rootPath); if (!rootFile.isDirectory()) { System.out.println("要搜索的路径有误!"); return; } System.out.println("请输入要搜索的查询词: "); String key = scanner.next(); // 进行递归 scan(rootFile, key); } private static void scan(File rootFile, String key) { if (!rootFile.isDirectory()) { return; } File[] files = rootFile.listFiles(); if (files == null || files.length == 0) { return; } for (File f : files) { if (f.isFile()) { // 进行后续的查询操作 doSearch(f, key); } else { // 进行递归 scan(f, key); } } } private static void doSearch(File f, String key) { // 打开文件, 读取文件内容, 判定文件内容是否包含 key StringBuilder stringBuilder = new StringBuilder(); try (Reader reader = new FileReader(f)) { char[] buffer = new char[1024]; while (true) { int n = reader.read(buffer); if (n == -1) { break; } String s = new String(buffer, 0, n); stringBuilder.append(s); } } catch (IOException e) { e.printStackTrace(); } if (stringBuilder.indexOf(key) == -1) { // 未找到 return; } // 找到了 System.out.println("找到匹配的文件: " + f.getAbsolutePath()); }}
此处这里的代码逻辑 效率是非常低的,每次查询都会涉及大量的硬盘IO操作,尤其是硬盘上可能有一些大的文件
这种思路,不能适应频繁查询场景,也不能适应目录中文件数目特别多,特别大的场景(实现搜索引擎)