前言
小亭子正在努力的学习编程,接下来将开启javaEE的学习~~
分享的文章都是学习的笔记和感悟,如有不妥之处希望大佬们批评指正~~
同时如果本文对你有帮助的话,烦请点赞关注支持一波, 感激不尽~~
目录
前言
前驱知识
文件:
目录:
文件路径(Path):
文件类型
Java 中操作文件
1.文件系统操作
File类的属性
File类的构造方法
File类的常用方法
2.文件内容操作
字符流 和字节流
输入输出流:
InputStream 概述
FileInputStream 概述
InputStream的方法:
OutputStream 概述
小案例:
遍历目录,查找包含某个关键词的文件
前驱知识
文件:
我们先来认识狭义上的文件(file)。针对硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公桌上的一份份真实的文件一般。
目录:
随着文件越来越多,对文件的系统管理也被提上了日程,如何进行文件的组织呢,一种合乎自然
的想法出现了,就是按照层级结构进行组织 —— 也就是我们数据结构中学习过的树形结构。这样,一种专门用来存放管理信息的特殊文件诞生了,也就是我们平时所谓文件夹(folder)或者目录(directory)的概念。
【一般在计算机中我们将文件夹叫做 目录】
![](https://img-blog.csdnimg.cn/e21d35a3b99e47a2867d81d538bf6410.png)
![](https://img-blog.csdnimg.cn/d2f32be0e4b54355a15cfc3b96d0b06a.png)
![](https://img-blog.csdnimg.cn/56dfb39568ea44a19b4688df40ca8d61.png)
![](https://img-blog.csdnimg.cn/93913889314e49a9a9ace42480fc9529.png)
文件路径(Path):
![](https://img-blog.csdnimg.cn/ca446111627b46d8ae1b25585acb4e3e.png)
咱们电脑的文件存储的形式就像一颗树一样,从根节点出发,向下分叉衍生
![](https://img-blog.csdnimg.cn/213afa1693604e94b1ea934a55fce7ff.png)
-
文件路径:就是从树根结点出发,沿着树杈,一路向下走,达到目标寄文件,这个过程中经过的文件就是路径。
-
绝对路径:从盘符开始,一层一层往下找,这个过程得到的路径。
-
相对路径:从给定的某个目录出发,一层一层往下找,,这个过程得到的路径。
【注意:区分的重点在于找准基准目录(工作目录)是哪个】
特殊标识符:
- . 相对路径中,表示当前目录
- .. 相对路径中,表示当前目录的上级目录
- 路径中的每级目录之间用一个路径分隔符隔开,/ or \\ or \
【路径分隔符和系统有关:
●windows和DOS系统默认使用“\”来表示
●UNIX和URL使用“/”来表示】
【注意:
- 在Windows 中,路径和文件是一一对应的,任何一个文件,对应的路径是惟一的。但是在Linux中不一定。
- word,Excel,是二进制文件.csv 是文档文本】
文件类型
文件类型分为:
文本文件:存储的是文本,内容是由ASCII码构成的。
二进制文件:存储的是二进制数据。(内容不受限制)
【一个简单粗暴的区分方法:打开文件,你能看懂的就是文本文件,乱七八糟的你看不懂的就是二进制文件。注意,word写的docx,Excel的文件是二进制。】
Java 中操作文件
1.文件系统操作
创建文件,删除文件,重命名文件,创建目录
Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意,有 File 对象,并不代表真实存在该文件
File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流,下面会说到
File对象可以作为参数传递给流的构造器
File类的属性
修饰符及类型 |
属性 |
说明 |
static String |
pathSeparator |
依赖于系统的路径分隔符,String 类型的表示 |
static char |
pathSeparator |
依赖于系统的路径分隔符,char 类型的表示 |
File类的构造方法
签名 |
说明 |
File(File parent, String child) |
根据父目录 + 孩子文件路径,创建一个新的 File 实例 |
File(String pathname) |
根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者 相对路径 |
File(String parent, String child) |
根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用 路径表示 |
File类的常用方法
![](https://img-blog.csdnimg.cn/9f7c58ba8c7a4f48b2722d90902a90e9.png)
2.文件内容操作
针对文件内容进行读和写
补充知识:
流
流的分类:
操作数据单位:字节流(8 bit)、字符流(16 bit)
数据的流向:输入流、输出流
流的角色:字节流、处理流
体系结构:抽象基类、节点流、处理流
节点流:直接从数据源或目的地读写数据
处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能
【本文我们主要讨论字节流,字符流,输入流和输出流】
字符流 和字节流
- 针对 文本文件,提供了一组类,统称为 “字符流”,(典型代表:Reader(读),Writer(写)),读写基本单是:字符
- 针对 二进制文件,提供了一组类,统称为“字节流”,(典型代表:InputStream(读),OutputStream(写))读写基本单位是:字节
输入输出流:
输入:Reader,InputStream
输出:Writer,OutputStream】
输入输出的方向注意要以cpu为基准去判断
![](https://img-blog.csdnimg.cn/34b9742e87224802a504df6c8ce0a72a.png)
InputStream 只是一个抽象类,不仅可以读写文件,还可以读写背的,比如网卡。要使用还需要具体的实现类。关于 InputStream 的实现类有很多,基本
可以认为不同的输入设备都可以对应一个 InputStream 类,我们现在只关心从文件中读取,所以使用它的子类FileInputStream
构造方法
签名 |
说明 |
FileInputStream(File file) |
利用 File 构造文件输入流 |
FileInputStream(String name) |
利用文件路径构造文件输入流 |
代码实现一下(注意:这种方式不建议用,因为return,异常之类的情况可能会导致close()执行不到,推荐写法在下面)
![](https://img-blog.csdnimg.cn/e1db8cdc93cb4535a9b9860eb38ab737.png)
推荐写法:
![](https://img-blog.csdnimg.cn/308ce1cfe4de44fd968270d39f609ec8.png)
注意:
1.可能会因为权限问题和中文文件名报错,大家实现代码的时候注意一下
2.close()方法千万不能忘记写。因为在java中,文件这里的资源(主要是文件描述符表)需要手动释放。
文件描述符:
记载当前进程都打开了那些文件。
每次打开一个文件,就会在这个表里,这个表记载了当前进程打开了哪些文件,每次打开一个文件就会在这个表里申请空间。这个表的空间是有限的,不停地申请空间,又不释放,空间会满,一旦满了,继续打开文件就会打开失败,严重的还会文件资源泄露。
返回值类 型 |
方法签名 |
说明 |
int |
read() |
读取一个字节的数据,返回 -1 代表已经完全读完了 |
int |
read(byte[] b) |
最多读取 b.length 字节的数据到 b 中,返回实际读到的数 量;-1 代表以及读完了 |
int |
read(byte[] b, int off, int len) |
最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返 回实际读到的数量;-1 代表以及读完了 |
void |
close() |
关闭字节流 |
代码示例:
读操作,字节流
public class IODemo1 {
public static void main(String[] args) throws IOException {
// //文件打开 //此处需要抛异常
// InputStream inputStream = new FileInputStream("D:/yunna/Desktop/笔记草稿/IO测试文件");
// inputStream.close();//关闭文档,千万不能忘了
//推荐使用下面这种写法, 因为InputStream实现了Closeable这一特定的接口,这样写try会自动帮忙调用.
try( InputStream inputStream = new FileInputStream("D:\\code\\text.txt")){
//读文件
while(true){
int b = inputStream.read();
if (b==-1){
//读到末尾了,结束循环
break;
}
System.out.println(b);
}
}
}
}
![](https://img-blog.csdnimg.cn/9429775643f54c6ea2564cc5f6b8d678.png)
读操作,字符流
public class IODemo3 {
public static void main(String[] args) {
try(Reader reader = new FileReader("D:\\code\\text.txt")){
while(true){
int c = reader.read();
if(c==-1){
break;
}
char ch = (char) c;
System.out.println(ch);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
![](https://img-blog.csdnimg.cn/6c7a3a7a10c147fa965c36d48fb05c82.png)
OutputStream 概述
OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中,所以使用 FileOutputStream
方法
修饰 符及 返回 值类 型 |
方法签名 |
说明 |
void |
write(int b) |
写入要给字节的数据 |
void |
write(byte[] b) |
将 b 这个字符数组中的数据全部写入 os 中 |
int |
write(byte[] b, int off, int len) |
将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个 |
void |
close() |
关闭字节流 |
void |
flush() |
重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为 了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的 一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写 入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的 数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置, 调用 flush(刷新)操作,将数据刷到设备中。 |
代码示例:写操作,字节流
public class IODemo2 {
//写操作
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("D:\\code\\text.txt")){
outputStream.write(97);//注意这个写入的是ascii码值
outputStream.write(98);
outputStream.write(99);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
![](https://img-blog.csdnimg.cn/5fe6e29d356c4e9fb9c932d4b0a069c9.png)
写操作,字符流
public class IODemo4 {
public static void main(String[] args) {
try(Writer writer = new FileWriter("D:\\code\\text.txt")) {
writer.write(65);
writer.write(66);
writer.write(67);
writer.write(68);
} catch (IOException e) {
e.printStackTrace();
}
}
}
![](https://img-blog.csdnimg.cn/9ec3ab8897e34cfba7e7104036f0884e.png)
小案例:
遍历目录,查找包含某个关键词的文件
![](https://img-blog.csdnimg.cn/d5b9c1b29cc24b05af0f6982a2fb4ad8.png)
步骤:
//让用户先指定一个要搜索的根目录
//让用户输入一个要查询的词
//递归进行目录/文件的遍历(目录结构是一颗N叉树)
注意:这种方法不高效,但是小规模搜索还是可以的
import java.io.*;
import java.util.Scanner;
public class IODemo5 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//先让用户指定一个要搜索的根目录
System.out.println("请输入需要搜索的目录:");
File rootDir = new File(scanner.next());
if (!rootDir.isDirectory()){
System.out.println("输入有误,您输入的目录不存在!");
return;
}
//让用户输入一个要查询的词
System.out.println("请输入要查询的词");
String word = scanner.next();
//递归的进行目录文件的遍历
scanDir(rootDir,word);
}
private static void scanDir(File rootDir,String word){
//列出当前的rootDir 中的内容,没有内容,直接递归结束
File[] files = rootDir.listFiles();
if (files == null){
//当前rootDir 是一个空目录,直接结束不用递归
return;
}
//目录里有东西,就遍历每个元素
for (File f: files){
System.out.println("当前搜索到:"+ f.getAbsoluteFile());
if (f.isFile()){
//是普通文件,就打开文件,比较看是否包含关键词
String content = readFile(f);
if (content.contains((word))){
System.out.println(f.getAbsoluteFile()+"包含要查找的关键字");
}
}else if(f.isDirectory()){
//是目录,就进行递归操作
scanDir(f,word);
}else{
//不是普通文件也不是目录文件,直接跳过
continue;
}
}
}
private static String readFile(File f) {
//读取文件的整个内容,返回出来,匹配的是字符串,所以用字符流
StringBuilder stringBuilder = new StringBuilder();
try (Reader reader = new FileReader(f)){
//一次读取一个字符,把读到的结果拼装到StringBuilder中,统一转成String
while (true){
int c = reader.read();
if(c== -1){
break;
}
stringBuilder.append((char)c);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return stringBuilder.toString();
}
}
![](https://img-blog.csdnimg.cn/a6d99cf218954d089d7a90f58c57527c.png)
注意:
上述代码没有考虑文件特别大,内存存不下的情况,如果真遇到这种情况,可以考虑把文件分块读取
![](https://img-blog.csdnimg.cn/0f7f5ccb38a54452afcfc9433d95de11.png)
说明:
![](https://img-blog.csdnimg.cn/323ac2897efc4f519b532c69da26ebc5.png)