目录
一 文件
1.文件的定义
1.狭义的文件
指的是硬盘上的文件和目录(文件夹)。
2.广义的文件
泛指计算机中很多的软硬件资源。操作系统中把很多的硬件设备和软件资源抽象成了文件,按照文件的方式来统一管理。
3.文件的类型
文本文件:存的是文本、字符串
二进制文件:存的是二进制数据,不一定是字符串了
4.路径
绝对路径:以C: 、D:盘符开头的路径
相对路径:以当前所在的目录(工作目录)为基准,以 . 或 .. 开头找到指定路径
2.File类
Java对于文件的操作
1.针对文件系统操作:文件的创建,删除,重命名...
2.针对文件内容操作:文件的读和写
二 文件系统的操作
Java标准库中提供了File这个类
😶🌫️属性
修饰符及类型 | 属性 | 说明 |
static String | pathSeparator | 依赖于系统的路径分隔符,String 类型的表示 |
static char | pathSeparator | 依赖于系统的路径分隔符,char 类型的表示 |
😶🌫️构造方法
😶🌫️常用方法
deleteOnExit程序退出的时候,自动删除-->程序中需要使用一些"临时文件"的时候需要用到
三 文件内容的操作
针对文件内容,是使用"流对象"来进行读写的。比如通过水龙头接100ml水,可以一次接100ml,可以分两次接50ml,也可以分10次接10ml。
如果从文件中读100个字节,可以一次性读100字节,可以分两次读50字节,也可以分10次读10字节,写文件同理。
Java标准库中的流对象从类型上分为两大类:字节流(操作二进制数据)和字符流(操作文本数据)。
操作步骤:
1.打开文件(构造对象)
2.关闭文件(close)
3.读文件(read)-->针对InputStream/Reader
4.写文件(write)-->针对OutputStream/Writer
InputStream/OutputStream/Reader/Writer都是抽象类,不能直接new
1.字节流
😶🌫️InputStream
❤️理解read的行为和返回值:
read会尽可能的把参数传进来的数组填满,上面希望读取的长度是1024,read会尽可能的读取1024个字节到数组中。但实际上文件剩余的长度有限,如果剩余的长度大于1024个字节,那么都会被1024字节填上;反之剩余长度不足1024,那么有多少就填多少,read方法会返回实际读取长度。
❤️buffer存在的意义:为了提高IO的效率
单次IO操作要访问硬盘/IO设备,比较消耗时间。如果频繁进行IO操作耗时就更多了。如果能缩短IO操作的次数,就可以提高整体效率。
第一个版本的代码,一次只读一个字节,循环次数多,read次数也多,效率低耗时多。
第二个版本的代码,一次读1024个字节,循环次数大大降低,read次数少了,效率变高耗时减少。
注意区分:cache(缓存),buffer(缓冲区)
😶🌫️OutputStream
关于close:
进程内容提到过:在内核中,使用PCB这样的数据来表示进程
一个线程对应一个PCB
一个进程可以对应1-n个PCB
PCB中有一个重要的属性->文件描述符表(相当于一个数组),记录了进程打开了哪些文件
如果close没有写会怎么样?
如果没有close,意味着文件描述符表很快就会被占满了(这个数组不会自动扩容,存在上限),那么之后再次打开文件就会失败。
怎么确保close被执行到?
一般来说close是要执行的,但是如果一个程序里的文件自始至终都要使用不关闭问题也不大。因为随着进程结束,PCB销毁,文件描述符表也被销毁了,对应的资源也就被操作系统自动回收了。
2.字符流
😶🌫️Reader
😶🌫️Writer
😶🌫️Scanner
四 练习
1.扫描指定目录,并找到名称中包含指定字符的所有文件(不包含目录),并且询问用户是否删除文件。
步骤:
1.用户输入一个目录
2.判断用户输入的目录是否有效
3.用户输入要删除的文件名
4.对指定目录进行扫描,递归
注意:
public class IODemo {
private static Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
//1.让用户输入一个指定搜索的目录
System.out.println("请输入要搜索的路径:");
String basePath = sc.next();
//2.针对用户的输入进行简单判断
File root = new File(basePath);
if(!root.isDirectory()){
//路径不存在或者只是一个普通文件,则无法进行搜索
System.out.println("输入的目录有误!");
return;
}
//3.再让用户输入一个要删除的文件名
System.out.println("请输入要删除的文件名");
String nameTodelete = sc.next();
/*针对指定路径进行扫描,递归操作
* 先从根目录出发,如果当前目录包含要删除的文件就删除,否则就到下一个
* 如果当前位置包含了一些目录,再对子目录进行递归*/
scanDir(root, nameTodelete);
}
private static void scanDir(File root, String nameTodelete) {
System.out.println("[scanDir]" + root.getAbsolutePath());
//1.先列出root下的文件和目录
File[] files = root.listFiles();
if(files == null){
return;//空目录
}
//2.遍历当前列出结果
for(File f : files){
if(f.isDirectory()){
//如果是目录就进一步递归
scanDir(f, nameTodelete);
}else{
//如果是普通文件则判断要不要删除
if(f.getName().contains(nameTodelete)){
System.out.println("确认是否要删除" + f.getAbsolutePath() + "吗?");
String choice = sc.next();
if(choice.equals("y") || choice.equals("Y")){
f.delete();
System.out.println("删除成功!");
}else{
System.out.println("删除取消!");
}
}
}
}
}
}
2.进行普通文件的复制
步骤:
1.用户输入源和目标路径
2.检查源文件是否存在并且是一个文件
3.检查目标文件是否存在
4.进行拷贝操作
public class IODemo12 {
public static void main(String[] args) {
//1.输入两个路径:源路径和目标路径(从哪来拷贝到哪去)
Scanner sc = new Scanner(System.in);
System.out.println("要拷贝的文件:");
String srcPath = sc.next();
System.out.println("要拷贝到:");
String destPath = sc.next();
File srcFile = new File(srcPath);
if(!srcFile.isFile()){
//如果源不是文件而是目录或不存在,不做任何操作
System.out.println("输入的源路径有误!");
return;
}
File destFile = new File(destPath);
if(destFile.isFile()){
//如果目标已经存在也不能拷贝
System.out.println("输入的目标路径有误!");
return;
}
//2.进行拷贝操作
try(InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)){
//进行文件读写操作
while(true){
int b = inputStream.read();
if(b == -1){
break;
}
outputStream.write(b);
}
}catch (IOException e){
e.printStackTrace();
}
}
}