javaee初阶 文件IO
目录
一.概念
1.树形结构组织 与 目录
2.⽂件路径(Path)
3.数据类型
4.存储与读取
二.File(类)
1.文件操作
2.方法介绍
1>.属性
2>.构造方法
a.File(File parent,String child)
b.File(String pathname)
c.File(String parent,String child)
3>.方法
a. getParent() 与 getName()
b.getAbsolutePath() 与 getCanonicalPath()
c.exists()
d. isDirectory() 与 isFile()
e.createNewFile()
f.delete() 与 deleteOnExit()
g. list() 与 listFiles()
h. mkdir() 与 mkdirs()
i. renameTo()
(1)介绍:
(2).代码演示
j.canRead() 与 canWrite()
3.文件内容操作
1>:流
2>:两大类派
(1).字节流
(2).字符流
(3).对应的类
3>:字节流方法
(1).InputStream概述
a. read() 方法
b.read(byte[] b) 方法
c.read(byte[] b,int off,int len)
(2).OutputStream概述
a. write(int b)编辑
b.write(byte[] b)
c.write(byte[] b,int off,int len)
4>:字符流方法
(1).FileReader概述
(2).FileWriter概述
5>:(重)close 方法
(1).具体介绍
(2).代码实现
6>:scanner 与 system.out.println()
(1).scanner 与 InputStream
(2).PrintWriter
三.运用
1>:题目一
2>:题目二
3>:题目三
说到文件大家都不陌生吧,基础的简单概念就不说了
一.概念
1.树形结构组织 与 目录
正常情况,一个文件夹里有许多文件,而这些文件又有许多文件,类似于二叉树 一个根节点有许多节点,这些节点又有许多节点,所以称为数型结构组织,可以把文件夹称为目录
2.⽂件路径(Path)
路径:用于描述某个文件/目录所在的位置
(1).绝对路径:从根目录一直到目标所在的文件的路径
比如: qq的文件所在地 D:\Program Files (x86)\Tencent\QQ 就是绝对路径
(2).相对路径:从基准路径开始,走哪个路径可以到目标所在地
比如:以 D:\Program Files (x86) 为基准路径,那么 .\Tencent\QQ 就是相对路径
比如:以 D:\Program Files (x86)\Tencent 为基准路径,那么 .\QQ 就是相对路径
(这里 . 表示当前目录,从当前目录开始 ; 相对路径可以写成QQ,省略 .\)
(3).格式:
a. (..)表示当前文件上一级,例如: ..\QQ 表示QQ上一级Tencent目录
b.路径之间的隔离符用 \(反斜杠) 或者 /(正斜杠) 都可以,最好用正斜杠,因为反斜杠你还要处理转义字符
3.数据类型
文件的数据类型有许多种,如:exe jpg 音频文件 视频文件 等等,咱们分为: 文本文件 与 二进制文件,
文本文件:内容是字符串
文件文件:内容是二进制,人眼看是乱码
当我们进行IO操作是,需要确定目标文件是文本文件还是二进制文件,简单办法就是记事本打开,乱码的就是二进制文件,反之就是文本文件
4.存储与读取
文件是存在硬盘上的,而硬盘这种硬件设施上会存在一些寄存器,可以通过操控寄存器进行IO操作,而
硬盘提供了一组控制寄存器的"操作方法",因为复杂性,Java对其进行封装提供了一些API
二.File(类)
位于
1.文件操作
分为两类:
a.文件系统操作: 通过代码,对文件进行 创建 移动 复制 删除 等操作
b.文件内容操作: 读取文件,写入文件
文件系统(扩展知识):文件系统是操作系统的一个子模块,提供了一些方法,而文件资源管理器通过调用这些方法进行 文件创造 复制 移动 删除等操作,但这些方法底层用c实现,java对其进行封装为一个类File供我们使用
2.方法介绍
File类是java.io包下的类,有许多方法管理文件
1>.属性
这里属性 pathSeparator(路径分隔符) 有两种: / (正斜杠) \ (反斜杠),咱们无脑用 正斜杠即可
2>.构造方法
这里列举最常用的:
a.File(File parent,String child)
根据父亲目录创建file对象,并根据孩子路径创建一个fie对象
public static void main(String[] args) {// 假设路径 : D:/java/testFile file = new File("d:/java/");File file1 = new File(file,"test");}
b.File(String pathname)
·根据绝对路径创建file对象
File file = new File("D:/java/test");
·根据相对路径创建file对象
File file = new File("./test");
这个 . 代表当前程序运行的工作目录,可以点击终端查看基准路径:
c.File(String parent,String child)
以父亲路径与孩子路径创建file对象
File file = new File("D:/java/","./test");
3>.方法
a. getParent() 与 getName()
·绝对路径演示方法
File file = new File("D:/java/test");System.out.println(file.getParent());System.out.println(file.getName());
·相对路径演示方法:
File file1 = new File("./test");System.out.println(file1.getParent());System.out.println(file1.getName());
可以看出,两个路径都运行结果与预期一致
b.getAbsolutePath() 与 getCanonicalPath()
·绝对路径演示方法
File file = new File("D:/java/test");System.out.println(file.getAbsolutePath());System.out.println(file.getCanonicalPath());
·相对路径演示方法:
File file1 = new File("./test");System.out.println(file1.getAbsolutePath());System.out.println(file1.getCanonicalPath());
c.exists()
创建file对象的文件路径可以随便填,但file对象对应的文件不一定存在,所以需要判断才能进行IO
每创建test文件:
File file1 = new File("./test");System.out.println(file1.exists());// false
当我创建test文件后(直接在该文件夹新建-添加即可):
File file1 = new File("./test");System.out.println(file1.exists());// true
d. isDirectory() 与 isFile()
当test 是文件夹(目录):
File file1 = new File("./test");System.out.println(file1.isDirectory());// trueSystem.out.println(file1.isFile());// false
当 test 是 普通文件(存数据的文件,eg: txt jpg 等)
File file1 = new File("./test.txt");System.out.println(file1.isDirectory());// falseSystem.out.println(file1.isFile());// true
e.createNewFile()
创建的是"空"普通文件,具体类型取决于 后缀
File file1 = new File("./test.txt");// 创建 txt 文件file1.createNewFile();File file2 = new File("./photo.jpg");// 创建 jpg 文件file2.createNewFile();
可以在对应目录查看:
f.delete() 与 deleteOnExit()
这里删除表示直接从硬盘删除,不会放入回收站(小心操作!)
// 删除文件file1.delete();file2.delete();
g. list() 与 listFiles()
现在我的D盘文件目录如下:
先看list 方法:
File file1 = new File("d:/");// 获取 D 盘下所有目录名称String[] strFile = file1.list();// 因为 list 返回值可能为 nullif(strFile == null)System.exit(-1);for(String str : strFile)System.out.println(str);
listFiles 方法:
File file1 = new File("d:/");File[] strFile = file1.listFiles();if(strFile == null)System.exit(-1);for(File file : strFile)System.out.println(file.getName());
一般用这个,因为数数组每个元素都可以衔接File类方法
注(重) :
调用原生的 API方法不会永久删除,而会把文件放入盘符,在回收站进行手动删除
调用delete 方法直接进行永久删除,不进入盘符与回收站;
h. mkdir() 与 mkdirs()
mkdir():只能创建一层目录
File file1 = new File("./aaa");System.out.println(file1.isDirectory());boolean mkdir = file1.mkdir();System.out.println("创造是否成功:>"+mkdir);System.out.println(file1.isDirectory());
我们可以到文件里看是否创建成功:
mkdirs():创建多层目录
File file1 = new File("./aaa/bbb/ccc/ddd");System.out.println(file1.isDirectory());boolean mkdir = file1.mkdirs();System.out.println("创造是否成功:>"+mkdir);System.out.println(file1.isDirectory());
可以在文件看到:
i. renameTo()
(1)介绍:
方法功能为 从 源头source 移动到 目的地 dest;
若父亲路径一样,则重命名;否测进行移动
(2).代码演示
进行改名:
File source = new File("./aaa");// 创建 aaa 文件source.mkdir();File dest = new File("./bbb");// 因为 父亲路径相同,相当于重命名source.renameTo(dest);
粘贴:
File source = new File("./aaa");// 创建 aaa 文件source.mkdir();File dest = new File("./演示1/aaa");// 将 aaa 移动到 演示1 里面并重命名 aaasource.renameTo(dest);
j.canRead() 与 canWrite()
每个人对文件的权限不一样,所以提供这样的方法供查看权限
File dest = new File("./演示1/aaa");boolean retRead = dest.canRead();boolean retWrite = dest.canWrite();System.out.println("是否可读:>"+retRead);// trueSystem.out.println("是否可写:>"+retWrite);//true
3.文件内容操作
1>:流
文件的内容操作无非read 和 write,而操作系统提供的原生read 和 write 的API被java封装为流对象(一组类)
这里的流对象可以理解非常多的read 和 write 操作的集合
2>:两大类派
(1).字节流
读写时以字节为单位进行操作,适合二进制读写
(2).字符流
读写时以字符为单位进行操作,适合文本读写
(3).对应的类
3>:字节流方法
现在有个txt文件
(1).InputStream概述
a. read() 方法
public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("./test.txt");// 创建一次 流,相当于打开文件// InputStream 只涉及读操作,outputStream 只涉及写// 使用读操作 read()// 不清楚有多少个元素,所以循环while(true){int n = inputStream.read();if(n == -1)break;// 打印字节最好用 十六进制System.out.printf("%x\n",n);}
b.read(byte[] b) 方法
(1). 输出型参数
(2).代码与截图
public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("./test.txt");// read() 读硬盘的值,而硬盘的读取速度最慢了// 所以 read() 一次读一个字节太慢了// 一般用这种读取byte[] bytes = new byte[1024];int n = inputStream.read(bytes);// 如果read没读到值,最终会返回 -1,此时程序退出if(n == -1)System.exit(-1);for(int i = 0 ;i < n;i++) System.out.printf("%x\n",bytes[i]);}
c.read(byte[] b,int off,int len)
(1).方法解释
从 off 下标开始 读 len个元素放入byte中,并返回有效元素个数
(2).代码与截图
public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("./test.txt");byte[] bytes = new byte[3];int n = inputStream.read(bytes,0,3);// 如果read没读到值,最终会返回 -1,此时程序退出if(n == -1)System.exit(-1);for(int i = 0 ;i < n;i++) System.out.printf("%x\n",bytes[i]);}
(2).OutputStream概述
a. write(int b)
public static void main(String[] args) throws IOException {// 因为每次进行写入,都会清空之前的值接着重新写入// 所以第二个参数给 true,表示追加写OutputStream outputStream = new FileOutputStream("./test.txt",true);// 输入文件存放的数据// 虽然是 int,但取值范围 0-255,超出会忽略outputStream.write(97);outputStream.write(98);outputStream.write(99);outputStream.write(101);outputStream.write(102);}
b.write(byte[] b)
public static void main(String[] args) throws IOException {// 因为每次进行写入,都会清空之前的值接着重写写入// 所以第二个参数给 true,表示追加写OutputStream outputStream = new FileOutputStream("./test.txt",true);// 一次写入完整的字节数组byte[] bytes = new byte[]{97,98,99,100,101};// 这里不是输出型参数outputStream.write(bytes);}
c.write(byte[] b,int off,int len)
public static void main(String[] args) throws IOException {// 因为每次进行写入,都会清空之前的值接着重写写入// 所以第二个参数给 true,表示追加写OutputStream outputStream = new FileOutputStream("./test.txt",true);// 一次写入完整的字节数组byte[] bytes = new byte[]{97,98,99,100,101};// 这里不是输出型参数// 从 0下标开始输出 1个元素outputStream.write(bytes,0,1);}
4>:字符流方法
字符流的输出输入和上面介绍都一样,这里就不演示了
(1).FileReader概述
返回值都是int,且读取完会返回 -1
(2).FileWriter概述
5>:(重)close 方法
(1).具体介绍
流对象用完后要主动释放,而设计释放有手动释放 close 与自动释放GC垃圾回收管理器
close:释放申请的数据库连接 网络连接 文件等一切非内存资源
GC:回收内存中没有使用的 引用对象
总结:对内存而言,close()释放对外资源,GC释放对内资源
(2).代码实现
实现如下:
public static void main(String[] args) {// 因为文件对应的操作会报错,且 最终要及时执行close,所以 try-catch-finallyInputStream inputStream = null;try{inputStream = new FileInputStream("./test.txt");byte[] bytes = new byte[3];int n = inputStream.read(bytes,0,3);// 如果read没读到值,最终会返回 -1,此时程序退出if(n == -1)System.exit(-1);for(int i = 0 ;i < n;i++) System.out.printf("%x\n",bytes[i]);}catch(IOException e){e.printStackTrace();}finally {// 因为 可能文件描述表满了// 流对象创建失败,此时就不用释放资源if(inputStream != null){// 因为 close 也会抛异常,此时也要处理try{inputStream.close();}catch(IOException e){e.printStackTrace();}}}}
可以看到非常不美观,感觉太乱了,所以优化一下
public static void main(String[] args) {try(InputStream inputStream = new FileInputStream("./test.txt")){byte[] bytes = new byte[3];int n = inputStream.read(bytes,0,3);// 如果read没读到值,最终会返回 -1,此时程序退出if(n == -1)System.exit(-1);for(int i = 0 ;i < n;i++) System.out.printf("%x\n",bytes[i]);}catch(IOException e){e.printStackTrace();}}
而ReentrantLock类里面的无法这样写,因为类中没有实现 closeable接口
6>:scanner 与 system.out.println()
(1).scanner 与 InputStream
通过之前的学习,scanner是输入类,而InputStream也是输入类,那么又什么相似之处吗?
可以看到,scanner参数的 in 其实是个 值为空的InputStream对象,那么就可以主动给InputStream对象,让scanner从该对象读取元素
假设文件有值:我 爱你 1314,那么读取如下:
public static void main(String[] args) {try(InputStream inputStream = new FileInputStream("./test.txt")){Scanner scanner = new Scanner(inputStream);String a = scanner.next();String b = scanner.next();int c = scanner.nextInt();System.out.println(a+b+c);}catch (IOException e){e.printStackTrace();}}
所以 scanner + InputStream 对象,可以从指定对象读取值
(2).PrintWriter
PrintWriter 也是一个流中特殊的类,他负责把内容输入到 文件中
代码实现:
public static void main(String[] args) {try(PrintWriter printWriter = new PrintWriter("./test.txt")){// 这一步与 System.out.println() 很像// 不过一个把值输出到控制台// 另一个把值输出到文件中printWriter.println("我不爱你了");}catch (IOException e){e.printStackTrace();}}
三.运用
文件IO到此就差不多了,接下来做几道题练下手
1>:题目一
扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否要删除该⽂件
public class Demo2 {private static final Scanner scanner = new Scanner(System.in);public static void main(String[] args) {// 1.输入路径与文件System.out.print("请输入要查询寻的目录>:");String rootpath = scanner.next();System.out.print("请输入要查询的文件名称>:");String word = scanner.next();//2. 判断是否合理if(word.isEmpty()){System.out.println("文件名称错误"+word);return;}//3.判断目录是否存在或格式是否错误File file = new File(rootpath);if(!file.isDirectory()){// file 不存在 或者 file 是一个普通文件System.out.println("目录不存在或是普通文件");return;}// 4.开始查找search(file,word);}//因为目录是一个树形结构组织,所以查找用递归private static void search(File file, String word) {// 1.拿到 file 目录下所有文件File[] files = file.listFiles();//判断是否合理if(files == null)return;// 2.遍历每一个 file 对象,进行对应操作for (File newfile : files){// 判断是否为普通文件if(newfile.isFile()){// 进行删除if(newfile.getName().contains(word))trydelete(newfile);}else if(newfile.isDirectory()){// 开始递归继续 向文件内查找search(newfile,word);}else{// 其他情况,这里暂不处理}}}private static void trydelete(File file) {System.out.println("找到了,文件为>:"+ file.getAbsolutePath());int digtal = 3;while(digtal > 0){System.out.println("你还有"+digtal+"确定机会,请确定删除>:(yes / no)>:");String target = scanner.next();if(target.equalsIgnoreCase("no")) {System.out.println("取消删除");break;}digtal--;}if(digtal == 0){file.delete();System.out.println("删除成功");}}
}
2>:题目二
进⾏普通⽂件的复制(目录因为难度暂不考虑)
public class Demo3 {public static void main(String[] args) {// 1.输入 要复制的文件路径Scanner scanner = new Scanner(System.in);System.out.print("请输入要复制的文件路径>:");String sourcepath = scanner.next();// 2.判断合法性File source = new File(sourcepath);if(!source.exists()){System.out.println("复制的文件不存在");return;}if(source.isDirectory()){System.out.println("复制的文件是目录,不是普通文件");return;}// 3.输入 目的地System.out.print("请输入复制的文件目的地>:");String destpath = scanner.next();// 4.判断合法性// 无需判断存在性// 因为目的地的文件无论存在与否,都会进行覆盖或创建操作File dest = new File(destpath);if(dest.exists()){// 判断目标文件的父亲文件是不是目录if(!dest.getParentFile().isDirectory()){System.out.println("⽬标路径已经存在,并且是⼀个⽬录,请确认路径是否正确");return;}if(dest.isFile()){System.out.println("⽬录路径已经存在,是否要进⾏覆盖?y/n");String ans = scanner.next();if (!ans.equalsIgnoreCase("y")) {System.out.println("停⽌复制");return;}}}// 5.开始覆盖 / 创建// 对于输出流对象而言,创建时若文件存在会打开:若不存在,则会创建后打开try(InputStream sourcetread = new FileInputStream(sourcepath);OutputStream destOut = new FileOutputStream(destpath,true) ){while(true){byte[] bytes = new byte[1024];int n = sourcetread.read(bytes);if(n == -1)break;destOut.write(bytes,0,n);}}catch (IOException e){e.printStackTrace();}System.out.println("复制完成");}
}
可以打开对应文件看看
3>:题目三
扫描指定⽬录,并找到名称或者内容中包含指定字符的所有普通⽂件(不包含⽬录)
public class Demo4 {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);System.out.print("输入查询的根目录>:");String rootpath = scanner.next();// 判断是否合法File rootfile = new File(rootpath);if(!rootfile.exists()){System.out.println("根目录不存在");return;}if(!rootfile.isDirectory()){System.out.println("输入的路径不是一个目录");return;}System.out.print("输入查询的关键字>:");String word = scanner.next();// 递归寻找search(rootfile,word);}private static void search(File rootfile, String word) {// 拿到路径下所有文件File[] files = rootfile.listFiles();if(files == null)return;for(File newFile : files){if(newFile.isFile()){tryread(newFile,word);}else if(newFile.isDirectory()){search(newFile,word);}else{// 其他情况}}}private static void tryread(File newFile, String word) {// 1.判断是否在文件名if(newFile.getName().contains(word)){System.out.println("找到了,路径为>:"+newFile.getAbsolutePath());}// 2.判断是否在内容中存在// 这里因为要比较文件内容,所以按照 字符流 读取try(Reader reader = new FileReader(newFile)){StringBuilder stringBuilder = new StringBuilder();while(true){char[] chars = new char[1024];int n = reader.read(chars);if(n == -1)break;stringBuilder.append(chars,0,n);}// 判断是否存在if(stringBuilder.indexOf(word) >= 0){// 找到了System.out.println("找到了,路径为>:"+newFile.getAbsolutePath());}else{// 没找到return;}}catch (IOException e){e.printStackTrace();}}
}
文件就到此先结束了,注意:文件的操作非常危险,写代码实验时注意仔细看是否要删除
如果对你有帮助,点个赞吧!!