作者@ Autumn60
欢迎关注:点赞收藏✍️留言
微语: 这个世界永远比你想的要精彩,没有所谓的运气,只有绝对的努力,生活每天都有限量版,请带着你的坚持一起挑战!
目录
一、文件操作
1.文件系统
1.1文件的结构:
1.2路径:
二、文件内容操作,流对象
2.1 File 类
属性:
构造方法:
方法:
2.2 流
1.读文件:
InputStream 类:
2.写文件
OutputStream 类
3.关闭操作(释放资源)
平时谈到的“文件”,指的是硬盘上的文件
这章主要讲 创建文件,删除文件,重命名文件,创建目录
学习之前首先要知道什么是路径 和 文件的结构
计算机上的目录是有层级结构
在计算机里,保管储存文件是通过操作系统中的“文件系统”来实现的。
而文件系统中一般是通过树形结构来组织磁盘上的目录和文件的,文件是以树形结构来进行存储的,一颗N叉数
图例: 这就是树形结构
红框,框出来的就是路径,也就是文件系统上一个文件 / 目录 的具体位置 (目录也是文件夹);
文件路径:
就是从树根节点出发,沿着树杈,一路往下走,到达目标文件,此时这中间经过的内容;
Windows 都是从"此电脑" 起头的,所以在表示路径的时候,可以吧 "此电脑"省略,直接从 盘符 开始表示,所以一般看到的也就是从C盘 、D盘、F盘起头的这种,也就是上图中的样子。
实际表示路径,是通过一个字符串表示,每个目录之间使用 / 或者 \ 来分割 建议使用 / ,用反斜杠的话需要使用转义字符;
1.相对路径:
从给定的某个目录出发,一层一层的往下找,这个过程得到的路径就是相对路径
- 相对路径一定要明确,基准目录(工作目录)是啥
- 相对路径会随着你工作目录的变化而变化
- . 在相对路径中,是一个特殊符号,表示当前目录
- .. 也是特殊符号,表示当前目录的上级目录
图例:
相对路径的表示:假设此时工作目录是Code,则相对路径是./177/out /production
2.绝对路径
从盘符开始,一层一层的往下找,得到的路径,就是绝对路径
图例:
小结:
- 文件系统上,任何一个文件,对应的路径,也是唯一的,在Windows上(路径和文件是一一对应的)
- 也就是不会存在,两个路径相同,但是文件不同的情况!
- 在Linux 上,可能存在一个文件,有两个不同的路径找到它 (了解即可,本章不展开说)
- 但是在Windows上,不可能存在
通过一组类来操作;
如何进行文件系统操作呢?在Java标准库中提供了一个类,File 类!
- File 对象是硬盘上的一个文件的抽象表示;
- 文件是存储在硬盘上面的,直接通过代码操作硬盘,不太方便,就在内存中创建一个对应的对象,这时候操作这个内存中的对象,就可以间接的影响到硬盘的文件了(和电视剧的遥控板一样)
代码示例:
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
File file = new File("..\\hello-world.txt"); // 并不要求该文件真实存在
System.out.println(file.getParent());
System.out.println(file.getName());
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
}
}
执行结果:
针对文本文件,提供了一组类,统称为 “ 字符流 ” ,(典型代表,Reader,Writer)
针对二进制文件,提供了一组类,统称为 “ 字节流 ” ,(典型代表,InputStream,OutputStream)
流(Stream) /类别 :
什么是流?
而每种流又分为两种:
输入: reader , InputStream;
输出: Writer, OutputStream
代码:
public class Main {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("d/.cat.jpg");
}
}
- 如果在代码执行完不执行close 操作,就可能会导致 文件描述符表溢出问题;
- 在读文件时,如果出现一些问题,比如说 return 或者 抛出异常 ,就会导致 close 执行不到了,这时候可以通过try 和 finally 来保证执行close 操作
加入 try 和 finally 来保证 close 一定能被执行到;
public class Main {
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
try {
inputStream = new FileInputStream("d/.cat.jpg");
} finally {
//通过加入finally 来保证资源释放一定能被执行到
inputStream.close();
}
}
}
但是上述这种写法,太繁琐,而且不好看(优雅);
try(InputStream inputStream = new FileInputStream("d:/cat.txt")){ }
这种写法也 叫作 : try with resources 操作 ,其实就是带有资源的 try 操作, 会在 try 代码块结束,自动执行 close 关闭操作;
- 为什么会自动执行close 呢? 因为 InputStream 实现了一个特定的 interface 接口 Closeable;
- 也就是你实现了Closeable 这样的接口,就可以写成这样的语法
代码:
public class Main {
public static void main(String[] args) throws IOException {
try (InputStream inputStream = new FileInputStream("d:/cat.text")) {
//read 一次读的是一个字节,如果一个文件读取完了,到头了,再次read 就会返回 -1;
while(true) {
int b = inputStream.read();
if(b == -1) {
break;//代码走到这,也就是说明文件读到末尾了,结束循环即可;
}
System.out.println(b);
}
}
}
}
运行结果:
图中可以看出,每次读的是字节,因为每次读取一个字节,读出的这一串数据,就是每个字符的ASCII码!
方法:
代码:
a.一次写一个字节:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo1 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("d:/dog.txt")) {
//通过OutputStream 来进行写操作;
outputStream.write(97);
outputStream.write(98);
outputStream.write(99);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
执行完毕查看文件
b.一次写多个字节:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo1 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("d:/dog.txt")) {
//通过OutputStream 来进行写操作;
//一次性写了4个
byte[]tmp = new byte[]{97,98,98,97};
outputStream.write(tmp);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
结果:
close 即可
三个代码案例:
1.遍历目录,在里面的文件内容中查找(全文检索)
目标:
- 电脑上面有很多目录,目录里面又有很多的文件~而每个文件里面又有很多的内容
- 假设某些文件中,包含"hello world " 关键词;
- 这个程序就是找出哪些文件,是包含这个关键词的~
实现方式:
- 递归遍历目录 , 比如给定一个 d:/ 去递归 把这里包含的所有文件都列出来;
- 每次找到一个文件,都打开,并读取文件内容(得到 String);
- .在判定要查询的词,是否在上述文件内容中存在,如果存在,结果即为所求;
只适用于小规模的搜索;
如果要想用大规模,可以使用倒排索引;
代码:
import java.io.*;
import java.util.Scanner;
public class Demo3 {
public static void main(String[] args) {
//1.让用户输入一个想要搜索的根目录
//2.把输入的目录放进去,判断目录的输入的合法性;
//3.让用户输入一个想要查询的词;
//4.递归的进行目录/文件的遍历了;
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个你想要遍历的目录 : ");
File file = new File(sc.next());
//判断用户输入的目录是否是合法的!
if(!file.isDirectory()) {
System.out.println("输入目录有误");
return;
}
//2.让用户输入一个要查询的词;
System.out.println("请输入要查询的词");
String word = sc.next();
//3.递归的进行目录的遍历
scanDir(file,word);
}
private static void scanDir(File file, String word) {
//列出file中的内容,也就是查看输入的目录下面有没有文件,如果没有就直接结束;
//列出file中的内容,用数组接受
File[] files = file.listFiles();
if (files == null) {
//如果等于空,那就是啥也没有;
return;
}
for (File f : files) {
System.out.print(f.getAbsolutePath());
if(f.isFile()) {
//判断此刻拿到的是否是普通文件
//如果是普通文件,就打开,然后读取文件,判断是否包含;如果包含,就打印,不包含继续;
String cont = read(f);
if(cont.contains(word)) {
//如果包含就打印路径
System.out.println(f.getAbsolutePath() + "包含");
}
} else if(f.isDirectory()) {
//判断拿到的是否是目录文件
//目录文件就继续进行递归即可;
scanDir(file,word);
} else {
//如果上面两种都不是跳过即可;
continue;
}
}
}
private static String read(File f) {
StringBuilder stringBuilder = new StringBuilder();
try(Reader reader = new FileReader(f)) {
while(true) {
//把读出来的每一个字符全部放到stringBuilder;
int c = reader.read();
//读到-1就代表已经读取结束了;
if (c == -1) {
break;
}
stringBuilder.append((char)c);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
//返回即可
return stringBuilder.toString();
}
}
执行结果:
- 类似于一个 先序遍历
- 先访问根目录,把根目录里的内容都列出来
- 再一次遍历子目录
- 如果根目录没有子目录,就直接遍历完了
如果查询根目录太大的话就会报错; 读着读着超过内存容量就会报错;
本章节重点:
字节流:InputStream OutputStream
字符流 Reader Write