1.文件
1.认识文件
平时说的文件一般都是指存储再硬盘上的普通文件,形如txt,jpg,MP4,rar等这些文件都可以认为是普通文件,它们都是再硬盘上存储的。
在计算机中,文件可能是一个广义的概念,就不只是包含普通文件,还可以包含目录(把目录称为目录文件)文件可以是文本文档、图片、程序等等。文件通常具有点+三个字母的文件扩展名,用于指示文件类型(例如,图片文件常常以JPEG格式保存并且文件扩展名为.jpg)。
普通文件是保存在硬盘上的。
机械硬盘的基本构造:
- 盘片,存储数据的介质
- 磁头
针对硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时,
往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公桌上的一份份真实的文件一般。
文件除了有数据内容之外,还有一部分信息,例如文件名、文件类型、文件大小等并不作为文件的数据而存在,我们把这部分信息可以视为文件的元信息。
文件的分类(站在程序员的角度)
主要把文件分为两类
- 文本文件,里面存储的是字符,文本文件本质上也是存字节的,但是文本文件中相邻的字节在一起正好构成一个个字符。
- 二进制文件,里面存储的是字节
2.树型结构组织和目录
计算机里,保存管理文件,是通过操作系统中“文件系统”这样的模块来负责的。
随着文件越来越多,对文件的系统管理也被提上了日程,如何进行文件的组织呢,一种合乎自然的想法出现了,就是按照层级结构进行组织 —— 也就是我们数据结构中学习过的树形结构。这样,一种专门用来存放管理信息的特殊文件诞生了,也就是我们平时所谓文件夹(folder)或者目录(directory)的概念。
3. 文件路径
如何在文件系统中如何定位我们的一个唯一的文件就成为当前要解决的问题,但这难不倒计算机科学家,因为从树型结构的角度来看,树中的每个结点都可以被一条从根开始,一直到达的结点的路径所描述,而这种描述方式就被称为文件的绝对路径(absolute path)。
除了可以从根开始进行路径的描述,我们可以从任意结点出发,进行路径的描述,而这种描述方式就被称为相对路径(relative path),相对于当前所在结点的一条路径。
即使是定位到同一个文件,如果基准目录不同,此时相对路径也不同。
2.Java中的操作文件
1.文件系统相关的操作
文件系统相关的操作指的是通过”文件资源管理器“能够完成的一些功能。
功能:
- 列出目录中有哪些文件
- 创建文件
- 创建目录
- 删除文件
- 重命名文件
- …
在Java中提供了一个File类,通过这个类来完成上述操作
首先File类就描述一个文件/目录
File的构造方法,能够传入一个路径,来指定一个文件,这个路径可以是绝对路径也可以是相对路径
属性:
构造方法:
方法:
文件操作,也是一种输入输出,File这个东西也就在IO中
这样就可以定位到具体的文件,是以绝对路径的方式。
- 如果是通过命令行的方式(java.Demo),此时执行命令所在的目录,就是基准路径
- 如果是通过IDEA的方式来运行程序,此时基准路径就是当前Java项目所在的路径
- 把一个java代码打残war包,放到tomcat上运行
这种情况下基准路径就是tomcat的bin目录
一旦路径指定错了,很容易出现找不到的情况~
import java.io.File;
import java.io.IOException;
@SuppressWarnings({"all"})
public class Demo {
public static void main(String[] args) throws IOException {
File f = new File("E:/text.txt");
// 获取到文件的父目录
System.out.println(f.getParent());
// 获取到文件名
System.out.println(f.getName());
// 获取到文件路径
System.out.println(f.getPath());
// 获取到绝对路径
System.out.println(f.getAbsolutePath());
// 获取到绝对路径
System.out.println(f.getCanonicalPath());
System.out.println("===================");
File f2 = new File("./test.txt");
// 获取到文件的父目录
System.out.println(f2.getParent());
// 获取到文件名
System.out.println(f2.getName());
// 获取到文件路径
System.out.println(f2.getPath());
// 获取到绝对路径
System.out.println(f2.getAbsolutePath());
// 获取到绝对路径
System.out.println(f2.getCanonicalPath());
}
}
2.文件内容的相关操作
- 打开文件
- 读文件
- 写文件
- 关闭文件
针对文件内容的读写,java标准库提供了一组类~
首先按照文件的内容,分为两个系列
字节流对象,针对二进制文件,是以字节为单位进行读写的,读:InputStream,写:OutputStream。
字符流对象,针对文本文件,是以字符为单位进行读写的,读:Reader,写:Writer。
InputStream
read提供了三个版本的重载
- 无参版本:一次读一个字节
- 一个参数版本:一次读若干个字节,把读的结果放到参数指定的数组中,返回值就是读到的字节数
- 三个参数版本:一次读若干个字节,把读的结果放到参数指定的数组中,返回值就是读到的字节数,不是从数组的起始位置放置,而是从中间位置放置(off这个下标的位置)len表示最多能放多少个元素(字节)
IO操作失败的可能性是非常大的,另外硬盘也容易出现“坏道”
public class Demo2 {
public static void main(String[] args) {
// 构造方法中需要指定打开文件的路径
try {
// 1. 创建对象,同时也是在打开文件
InputStream inputStream = new FileInputStream("E://test.txt");
// 2. 尝试一个一个字节的读,把整个文件读完
while (true) {
int b = inputStream.read();
if (b == -1)
break;
System.out.println(b);
}
// 3. 读完之后要记得关闭文件,释放资源
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
更好的做法是把close放到finally里面
改进之后的代码
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@SuppressWarnings({"all"})
public class Demo3 {
public static void main(String[] args) {
// 构造方法中需要指定打开文件的路径
InputStream inputStream = null;
try {
// 1. 创建对象,同时也是在打开文件
inputStream = new FileInputStream("E://test.txt");
// 2. 尝试一个一个字节的读,把整个文件读完
while (true) {
int b = inputStream.read();
if (b == -1)
break;
System.out.println(b);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// 3. 读完之后要记得关闭文件,释放资源
try {
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
try (InputStream inputStream = new FileInputStream("E:/test.txt")){
// 一次读取若干个字符
while (true) {
byte[] buffer = new byte[1024];
int len = inputStream.read(buffer);
if (len == -1) {
// 如果返回-1 就读完了
break;
}
for (int i = 0; i < len; i++) {
System.out.println(buffer[i]);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
OutputStream
public static void main(String[] args) {
try (OutputStream outputStream = new FileOutputStream("E:/test.txt")) {
// outputStream.write(97);
// outputStream.write(98);
// outputStream.write(99);
byte[] buffer = new byte[]{97, 98, 99};
outputStream.write(buffer);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
@SuppressWarnings({"all"})
public class Demo4 {
public static void main(String[] args) {
try (Reader reader = new FileReader("E/test.txt")){
// 按照字符来读
while (true) {
char[] buffer = new char[1024];
int len = reader.read(buffer);
if (len == -1) {
break;
}
for (int i = 0; i < len; i++) {
System.out.println(buffer[i]);
}
String s = new String(buffer, 0, len);
System.out.println(s);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
// 按照字符来写
@SuppressWarnings({"all"})
public class Demo5 {
public static void main(String[] args) {
try (Writer writer = new FileWriter("E:?test.txt")){
writer.write("yjx");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
3.文件操作案例
扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)
import java.io.*;
import java.util.Scanner;
public class IODemo1 {
//扫描指定目录,并找到名称或者内容包含指定字符的所有普通文件
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//1.先让用户指定一个要搜索的根目录
System.out.println("请输入要扫描的根目录: ");
File rootDir = new File(scanner.next());
if(!rootDir.isDirectory()) {
System.out.println("输入有误,您输入的目录不存在!");
return;
}
//2.让用户输入一个人要查询的词
System.out.println("请输入要查询的词: ");
String word = scanner.next();
//3.递归的进行目录/文件的遍历
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) {
if (f.isFile()) {
//是普通文件
//打开文件,读取内容,比较看是否包含上述关键词
String content = readFile(f);
if(content.contains(word)) {
System.out.println(f.getAbsolutePath() + "包含要查找的关键字!");
}
}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 (IOException e) {
e.printStackTrace();
}
return stringBuilder.toString();
}
}