文件操作
1 认识文件
我们在平时说的文件一般都是指存储在硬盘上的普通文件,例如 txt,jpg,mp4,rar 等这些文件都可以认为是普通文件。
在计算机中,文件是一个广义的概念,不只是普通文件,还可以包括目录文件(就是我们所说的文件夹)。
1.1 普通文件
普通文件是保存在硬盘上的,硬盘有机械硬盘和固态硬盘,现在主要用的还是机械硬盘,虽然固态硬盘的传输速度快,但是固态硬盘比机械硬盘要贵的多。
机械硬盘
机械硬盘的基本构造是,盘片(存储数据的介质)和磁头。
机械硬盘一旦上电,里面的盘片就会高速运转,然后磁头再找到相应的数据。
由于机械硬盘的硬件结构,盘片的转速越高,读写速度越快,因为物理工艺的限制,转速不可能无限的高,目前机械硬盘的读写速度已经停滞了,现在主要是往大容量的方向发展。
固态硬盘
固态硬盘的结构和机械硬盘的结构完全不同,固态硬盘就像是一个大号的U盘,固态硬盘的读写速度要比机械硬盘的读写速度要高很多。但是固态硬盘的价格更贵。
目前的最好的固态硬盘的读写速度,可以和几十年前的CPU读写速度相比较了。
文件分类
站在程序员的角度可以把文件分成两类,一类是文本文件,一类是二进制文件。
文本文件:里面存储的是字符。
二进制文件:里面存储的是字节。
针对这两种文件在编程的时候会存在差异。
如果判断是哪一种文件,用记事本打开,如果打开之后是乱码,就是二进制文件,如果打开之后是文本就是文本文件。
1.2 目录结构
计算机里面保存和管理文件是通过 操作系统 中的 “文件系统” 模块来负责的。在文件系统中,一般是通过树形结构来组织磁盘上的文件和目录。
其中的树形结构,就和二叉树很像,不过这里的 树形 结构是 N 叉的。
在整个文件系统中,就是这种树形结构,普通的文件就是树的叶子结点,目录中可以包含普通文件,这个目录文件就相当于非叶子结点,有自己的子树。
路径
在操作系统中,就是通过 “路径” 这样的概念,来描述一个具体的文件/目录位置。由于是树形结构,其中根节点到每个节点的路径是唯一的。
路径有两种描述风格:
- 绝对路径:以盘符开头的
像:D:\java学习\java-topic-training - 相对路径:以 . 或者 … 开头的,其中 . 表示当前路径,而 … 表示当前路径的父目录(上级路径)。
谈到相对路径,必须有一个基准目录,相对路径就是从基准目录出发的。
2 Java 中操作文件
Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意,有 File 对象,并不
代表真实存在该文件。
File 类中的构造方法和方法
构造方法
名称 | 说明 |
---|---|
File(File parent, Stringchild) | 根据父目录 + 孩子文件路径,创建一个新的 File 实例 |
File(String pathname) | 根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径 |
File(String parent, Stringchild) | 根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示 |
方法
示例1
观察 get 系列的特点和差异
返回值类型 | 方法签名 | 说明 |
---|---|---|
String | getParent() | 返回 File 对象的父目录文件路径 |
String | getName() | 返回 FIle 对象的纯文件名称 |
String | getPath() | 返回 File 对象的文件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
import java.io.File;
import java.io.IOException;
public class TestDome {
public static void main(String[] args) throws IOException {
File file = new File("./text.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());
}
}
示例2
普通文件的创建、删除
返回值类型 | 方法签名 | 说明 |
---|---|---|
boolean | exists() | 判断 File 对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
import java.io.File;
import java.io.IOException;
public class TestDome2 {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\我很浮躁\\Desktop\\text.txt");
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
System.out.println("======================");
System.out.println("创建文件" + file.createNewFile());
System.out.println("======================");
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
}
}
示例3
观察 createNewFile() 的现象
返回值类型 | 方法签名 | 说明 |
---|---|---|
boolean | createNewFile() | 根据 File 对象,自动创建一个空文件。成功创建后返回 true |
boolean | delete() | 根据 File 对象,删除该文件。成功删除后返回 true |
void | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行 |
import java.io.File;
import java.io.IOException;
public class TestDome3 {
public static void main(String[] args) throws IOException {
File file = new File("some-file.txt"); // 要求该文件不存在,才能看到相同的现象
System.out.println(file.exists());
System.out.println(file.createNewFile());
System.out.println(file.exists());
System.out.println(file.delete());
System.out.println(file.exists());
}
}
示例4
观察目录的创建
返回值类型 | 方法签名 | 说明 |
---|---|---|
String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象表示 |
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
import java.io.File;
public class TestDome4 {
public static void main(String[] args) {
//这个目录不存在
File file = new File("C:\\Users\\我很浮躁\\Desktop\\text");
System.out.println(file.isFile());
System.out.println(file.isDirectory());
System.out.println(file.mkdir());
System.out.println(file.isFile());
System.out.println(file.isDirectory());
}
}
示例5
观察文件重命名
返回值类型 | 方法签名 | 说明 |
---|---|---|
boolean | renameTo(Filedest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操作 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
import java.io.File;
public class TestDome5 {
public static void main(String[] args) {
File file = new File("C:\\Users\\我很浮躁\\Desktop\\text");
File dest = new File("C:\\Users\\我很浮躁\\Desktop\\dest");
System.out.println(file.exists());
System.out.println(dest.exists());
file.renameTo(dest);
System.out.println(file.exists());
System.out.println(dest.exists());
}
}
3 文件内容的读写 —— 数据流
文件内容:
1)打开文件,2)读文件,3)写文件,4)关闭文件
针对文件的读写,Java标准库中提供了一组类,按照文件的内容分成两个系列:
- 字节流对象,针对二进制文件,是以字节为单位进行读写的。有两个抽象类,读:InputStream,写:OutputStream,我们往往使用的他们的子类,FileInputStream 和 FileOutputStream。
- 字符流对象,针对文本文件,是以字符为单位进行读写的。有两个抽象类,读:Reader,写:Writer,我们要使用的子类为 FileReader 和 FileWriter。
3.1 InputStream 概述
修饰符及返回值类型 | 方法名 | 说明 |
---|---|---|
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() | 关闭字节流 |
使用 read() 来读
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class TestDome6 {
public static void main(String[] args) {
InputStream inputStream = null;
try {
//1.打开文件
inputStream = new FileInputStream("E:\\test.txt");
while(true){
//2.读文件,直到把文件读完,文件读完返回-1;
int buffer = inputStream.read();
if(buffer == -1){
break;
}
System.out.println(buffer);
}
} catch (IOException e) {
//如果没有文件,和,读的时候异常就会报错。
e.printStackTrace();
}finally {
//3. 关闭文件。
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
如果按照上面这样写就写的很繁琐,Java中提供了一个机制(逻辑),代码如下:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class TestDome7 {
public static void main(String[] args) {
//1.打开文件
try(InputStream inputStream = new FileInputStream("E:\\test.txt")) {
//2.读文件
while(true){
int buffer = inputStream.read();
if(buffer == -1){
break;
}
System.out.println(buffer);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
虽然上面的代码中我们没有手动关闭文件的操作,但是 try / catch 走完就自动关闭文件。这样代码就简化了很多。
使用 read(byte[] b) 来读
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class TestDome8 {
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("E:\\test.txt")) {
byte[] buffer = new byte[1024];
while(true){
//这里面的 buffer 是输出型参数(把要带回来的值放到buffer数组中)
//其中的返回值为读取字符的长度。
int length = inputStream.read(buffer);
//当读取字符长度为 -1 时,文章内容读完了。
if(length == -1){
break;
}
for(int i=0; i<length; i++){
System.out.println(buffer[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.2 OutputStream 概述
修饰符及返回值类型 | 方法签名 | 说明 |
---|---|---|
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(刷新)操作,将数据刷到设备中。 |
使用 write(int b) 写入数据
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class TestDome9 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("E:\\test.txt")) {
outputStream.write('a');
outputStream.write('b');
outputStream.write('c');
outputStream.write('d');
outputStream.write('e');
outputStream.write('f');
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用 write(byte[]b) 写入数据
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class TestDome10 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("E:\\test.txt")) {
byte[] buffer = {'a', 'b', 'c', 'd'};
outputStream.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.3 Reader 概述
使用 read(int b) 来读
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class TestDome11 {
public static void main(String[] args) {
try(Reader reader = new FileReader("E:\\test.txt")) {
while(true){
int buffer = reader.read();
if(buffer == -1){
break;
}
System.out.println(buffer);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用 read(char[] buffer) 来读
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class TestDome14 {
public static void main(String[] args) {
try(Reader reader = new FileReader("E:\\test.txt")) {
char[] buffers = new char[1024];
while(true){
int length = reader.read(buffers);
if(length == -1){
break;
}
String str = new String(buffers, 0, length);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.4 Writer 概述
使用 write(char ch) 写入数据
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class TestDome13 {
public static void main(String[] args) {
try(Writer writer = new FileWriter("E:\\test.txt")) {
writer.write('a');
writer.write('a');
writer.write('a');
writer.write('a');
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用 write(String string) 写入数据
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class TestDome12 {
public static void main(String[] args) {
try(Writer writer = new FileWriter("E:\\test.txt")) {
writer.write("hello world");
} catch (IOException e) {
e.printStackTrace();
}
}
}
4 小程序练习
4.1 示例1
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件。
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class TestDome15 {
public static void main(String[] args) {
//输入参数
Scanner scanner = new Scanner(System.in);
System.out.println("请输出要查找的文件名:");
String rootDirPath = scanner.next();
System.out.println("请输入要删除的文件");
String delFile = scanner.next();
//1.打开文件目录
File rootDir = new File(rootDirPath);
if(!rootDir.isDirectory()){
System.out.println("输入的目录名非法");
}
//因为目录是树形结构,使用递归循环遍历每一个文件
scanFile(rootDir, delFile);
}
private static void scanFile(File rootDir, String delFile) {
//列出目录中的文件和目录
File[] files = rootDir.listFiles();
if(files == null){
return;
}
for (File f: files) {
//如果是文件就要判断是否和我们要找的文件名相同
if(f.isFile()){
if(f.getName().contains(delFile)){
//如果相同就删除文件
deleteFile(f);
}
}else if(f.isDirectory()){
//如果是目录就直接递归调用
scanFile(f, delFile);
}
}
}
private static void deleteFile(File f) {
try {
//判断文件是否要删除
System.out.println(f.getCanonicalPath() + "确定要删除这个文件吗(Y/N )");
Scanner scanner = new Scanner(System.in);
String buffer = scanner.next();
if(buffer.equals("y") || buffer.equals("Y")){
f.delete();
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.2 示例2
进行普通文件的复制
import java.io.*;
import java.util.Scanner;
public class TestDome16 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入文件所在的路径:");
String source = scanner.next();
System.out.println("请输入文件要复制到的路径");
String dest = scanner.next();
File sourceFile = new File(source);
if(!sourceFile.isFile()){
System.out.println("输入错误");
return;
}
try(InputStream inputStream = new FileInputStream(source)) {
try(OutputStream outputStream = new FileOutputStream(dest)){
byte[] buffers = new byte[1024];
while(true){
int length = inputStream.read(buffers);
if(length == -1){
break;
}
outputStream.write(buffers, 0, length);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.3 示例3
扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)
import java.io.*;
import java.util.Scanner;
public class TestDome17 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入要扫描的路径:");
String source = sc.next();
System.out.println("请输入要查找的文字:");
String word = sc.next();
File sourceFile = new File(source);
if(!sourceFile.isDirectory()){
System.out.println("输入非法");
return;
}
//扫描目录中的文件,递归调用
scanFile(sourceFile, word);
}
private static void scanFile(File sourceFile, String word) {
File[] files = sourceFile.listFiles();
if(files == null){
return;
}
for(File f: files){
//查看是的是文件,如果是文件就看看有没有我们指定的字符串
if(f.isFile()){
if(containWord(f, word)){
try {
//如果包含就打印文件的路径
System.out.println(f.getCanonicalPath());
} catch (IOException e) {
e.printStackTrace();
}
}
//如果是文件就递归调用
}else if(f.isDirectory()){
scanFile(f, word);
}
}
}
private static boolean containWord(File f, String word) {
StringBuffer stringBuffer = new StringBuffer();
try(Reader reader = new FileReader(f)) {
char[] buffers = new char[1024];
while(true){
int length = reader.read(buffers);
if(length == -1){
break;
}
stringBuffer.append(buffers, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
}
if(stringBuffer.toString().contains(word)){
return true;
}else{
return false;
}
}
}