本章主要讲解文件IO的基础知识与实际操作案例
冯诺依曼体系对文件的管理:
在操作系统内核中,提供了一个文件系统来专门管理文件.通过文件系统来有序地管理文件.
文件存储在硬盘中,计算机也是通过树形结构来管理文件的.
Java为文件/文件系统 提供了一系列的封装处理.
绝对路径是指文件在硬盘上真正存在的路径,(从树型结构的角度来看,树中的每个结点都可以被一条从根开始,一直到达的结点的路径所描述)
绝对路径的缺陷:在网页开发的过程中,我们为网页提供了绝对路径,可能在自己的计算机上浏览会一切正常,但是上传到Web服务器上浏览就很有可能不会显示图片了.
所谓相对路径,就是相对于自己的目标文件位置(任意结点出发,进行路径的描述)
Windows 操作系统上,会按照文件名中的后缀来确定文件类型以及该类型文件的默认打开程序,但是在Linux,IOS等其他操作系统,一般则没有这种习惯,不对文件进行精确的分类
文件由于是由操作系统进行管理的,所以一般操作系统给予用户可读,可写,可执行的权限.
Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述
有File类对象,不一定代表这个文件就存在
File(File parent,String child) :根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(String pathname) :根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File(String parent, String child):根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示
String | getParent() | 返回 File 对象的父目录文件路径 |
---|---|---|
String | getName() | 返回 FIle 对象的纯文件名称 |
String | getPath() | 返回 File 对象的文件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
boolean | exists() | 判断 File 对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
boolean | createNewFile() | 根据 File 对象,自动创建一个空文件。成功创建后返回 true |
boolean | delete() | 根据 File 对象,删除该文件。成功删除后返回 true |
void | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行 |
String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象表示 |
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操作 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
创建一个文件并获取文件的路径,是否存在,路径名称等
public class demo1 {
public static void main(String[] args) {
File file = new File("./test.txt");
try {
file.createNewFile();
System.out.println(file.getAbsoluteFile());
System.out.println(file.getCanonicalFile());
System.out.println(file.exists());
System.out.println(file.getParent());
System.out.println(file.getName());
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
判断文件类型:
public class demo2 {
public static void main(String[] args) throws IOException {
File file = new File("helloword.txt");
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
file.createNewFile();
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
}
}
文件删除:
public class demo3 {
//文件删除
public static void main(String[] args) throws IOException {
File file = new File("hello.txt");
file.createNewFile();
System.out.println(file.exists());
//立马删除
//file.delete();
//程序完全退出再删除
//file.deleteOnExit();
System.out.println(file.exists());
}
}
创建文件目录:
public class demo4 {
//创建文件目录
public static void main(String[] args) {
File file = new File("test/aa/1");
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println("=========================");
file.mkdirs();
System.out.println(file.exists());
System.out.println(file.isDirectory());
}
}
文件重命名:
import java.io.File;
import java.io.IOException;
public class demo5 {
//文件重命名:
public static void main(String[] args) throws IOException {
File file1 = new File("test1.txt");
File file2 = new File("test2.txt");
file1.renameTo(file2);
}
}
在java中,提供了两组类来提供对文件的操作:
第一组: InputStream OutputStream
以字节为单位的流,一般用来操作二进制文件
第二组: Reader Writer
以字符为单位的流,一般用来操作文本文件
以inputStream为例:
对于操作系统而言,直接地操作硬盘是十分不容易的,为了方便管理文件,我们可以在内存中构造一个对象去操作文件,像inputStream这类对象则称为句柄.
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() | 关闭字节流 |
签名 | 说明 |
---|---|
FileInputStream(File file) | 利用 File 构造文件输入流 |
FileInputStream(String name) | 利用文件路径构造文件输入流 |
使用InputStream读取文件内容:
第一种:
public class demo6 {
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
inputStream = new FileInputStream("test.txt");
while(true){
int len = inputStream.read();
if(len==-1){
break;
}
System.out.println(len);
}
inputStream.close();
}
}
text文件:
hello
运行结果:
第二种:
public class demo7 {
//使用read(byte [])
public static void main(String[] args) throws IOException {
File file = new File("test1.txt");
file.createNewFile();
byte b[] = new byte[1024];
InputStream inputStream = new FileInputStream(file);
while (true){
int len = inputStream.read(b);
if(len==-1){
break;
}
System.out.println(len);
}
}
}
上面两种情况潜在的bug:
如果在read过程中出现了异常,那么close()就可能执行不到,我们需要使用try and resource 来解决这个问题.
即在使用流时,放入try catch使用.
try ( InputStream inputstream = new FileInputStream( name)"test2.txt")){
byte[] b = new byte[1024];
int len = inputstream.read(b);
// .............
}}catch (IOException e){
e.printstackTrace();
}
在我们对进程的描述中:
一个进程是由一个PCB描述的.PCB中就有许多的属性.其中一个属性称为"文件描述符表"
我们每一次地打开文件,就会在文件描述符表占用一个位子.每一次关闭文件,就会在文件描述符表释放一个位子.
当然,文件描述符表并不是无穷大的,它依然是有上限的,如果一个进程不断地打开文件,占用的内存也不会多到挤爆内存,但是文件描述符表就会达到上限从而无法打开(文件描述符表无法扩容,但是可以配置).
因此我们需要使用完之后,要记得close去释放资源,避免出现程序后续文件无法打开的情况.
使用Reader:
public class demo8 {
//使用Reader流
public static void main(String[] args) {
File file = new File("test.txt");
try {
Reader reader = new FileReader(file);
char[] b = new char[100];
int len = reader.read(b);
for (int i = 0; i < len; i++) {
System.out.print(b[i]);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class demo9 {
//利用Scanner读文件
public static void main(String[] args) throws FileNotFoundException {
File file = new File("test.txt");
Reader reader = new FileReader(file);
Scanner scanner = new Scanner(reader);
String s = scanner.next();
System.out.println(s);
}
}
修饰符及返回值类型 | 方法签名 | 说明 |
---|---|---|
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(刷新)操作,将数据刷到设备中。 |
使用OutputStream与FileOutputStream来写文件
每一次重新写都会清除之前的内容再写入.
public class demo10 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("test.txt")){
String s = "hello,java";
// getBytes
outputStream.write(s.getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用Writer与FileWriter写文件
每一次重新写都会清除之前的内容再写入.
public class demo11 {
public static void main(String[] args) {
try(Writer writer = new FileWriter("test.txt")) {
String s = "hello,java2";
writer.write(s);
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用PrintWriter写文件:(有效避免缓存区问题)
import java.io.*;
public class demo12 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("test.txt")){
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println("aaaaa");
printWriter.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class demo13 {
//扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入扫描的路径:");
String dir = scanner.next();
File file = new File(dir);
if(!file.exists()){
System.out.println("要扫描的路径不存在");
return;
}
System.out.println("请输入你要删除的指定字符:");
String todelete = scanner.next();
scanDir(file,todelete);
}
public static void scanDir(File file,String todelete){
try {
System.out.println(file.getCanonicalPath());
} catch (IOException e) {
e.printStackTrace();
}
File[] files = file.listFiles();
if(files == null){
return;
}
for (int i = 0; i < files.length;i++){
if(files[i].isDirectory()){
scanDir(files[i],todelete);
}else{
trydelete(files[i],todelete);
}
}
}
private static void trydelete(File file, String todelete) {
Scanner scanner = new Scanner(System.in);
if(file.getName().contains(todelete)){
System.out.println("是否要删除该文件内容:" + file.getAbsoluteFile());
System.out.println("如果确定删除,输入y,否则输入n");
String s = scanner.next();
if(s.equals("y")){
file.delete();
}
}
}
}
复制文件
import java.io.*;
import java.util.Scanner;
public class demo14 {
//复制文件:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你要复制的文件路径");
String filename = scanner.nextLine();
File file = new File(filename);
if(!file.isFile()){
System.out.println("输入错误,不是普通文件");
}
System.out.println("请输入你要复制进的文本文件路径");
String filename1 = scanner.nextLine();
File file1 = new File(filename1);
if(!file1.isFile()){
System.out.println("输入错误,不是普通文件");
}
Mycopy(file,file1);
}
private static void Mycopy(File file, File file1) {
try(InputStream inputStream = new FileInputStream(file)) {
try(OutputStream outputStream = new FileOutputStream(file1)){
byte[] b = new byte[1024];
while(true){
int len = inputStream.read(b);
if(len==-1){
break;
}
outputStream.write(b,0,len);
}
System.out.println("复制成功");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
遍历目录, 看某个输入的词是否在文件名或者文件内容中存在.
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class demo15 {
//遍历目录, 看某个输入的词是否在文件名或者文件内容中存在.
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要搜索的路径:");
String filename = scanner.nextLine();
File file = new File(filename);
if (!file.exists()) {
System.out.println("目录或者文件不存在");
return;
}
if (!file.isDirectory()) {
System.out.println("该路径不是目录");
return;
}
String hava = scanner.nextLine();
scanDir(file, hava)
}
private static void scanDir(File file, String hava) {
if (file == null) {
return;
}
File[] files = file.listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
scanDir(files[i], hava);
} else {
tocheck(file[i], hava);
}
}
}
private static void tocheck(File f, String hava) throws IOException {
// 是不是文件名的一部分
if (f.getName().contains(hava)) {
System.out.println("找到文件名匹配的文件: " + f.getCanonicalPath());
return;
}
// 是不是文件内容的一部分
try (InputStream inputStream = new FileInputStream(f)) {
// 把文件内容整个的都读出来.
StringBuilder stringBuilder = new StringBuilder();
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNextLine()) {
stringBuilder.append(scanner.nextLine());
}
// 读取完毕了.
if (stringBuilder.indexOf(hava) >= 0) {
System.out.println("找到文件内容匹配的文件: " + f.getCanonicalPath());
return;
}
}
}
}
这种知识需要多看多写,不能不复习,也不能只看,要多实践…