文件路径、文件系统操作、字节流字符流、文件内容操作、自己实现文件查找 删除 复制、IO报错:拒绝访问

目录

一、什么是文件

文件的分类

文件路径 

二、文件系统操作

四、文件内容操作 

字节流字符流 

1)字节流(二进制文件)

 InputStream概述

 FileInputStream 概述

OutputStream 概述 

2)字符流(文本文件)

五、实现文件查找,删除,复制

根据文件内容查找:

删除:

复制:

IO拒绝访问 


一、什么是文件

文件 就是在计算机的硬盘中躺着的,这些文件都是一个个单独的个体。 

⽂件除了有数据内容之外,还有⼀部分信息,例如⽂件名、⽂件类型、⽂件⼤⼩等并不作为⽂件的数 据⽽存在,我们把这部分信息可以视为⽂件的元信息。

我们文件的存储 是依靠树型结构来存储的 

目录:相当于文件夹,可能很多个文件放一起的

文件:一个单独的个体

文件的分类

即使是普通文件,存储的数据也不尽相同。

所以我们依靠数据的类型,把他们又分为 文本文件 和 二进制文件

文本文件:里面的内容在码表上“有据可查”,能查到对应的 

二进制文件:里面的内容在码表上查不到,就是一堆乱码

啥是码表?

因为计算机只看得懂二进制,所以要把字解析给计算机看的,为了统一,就是根据码表来解析的。

码表也有很多种,最常见的是ASCII码,解析英文字母。但是我们的中文啊,还有其他的特殊字符,就用不了,就需要UTF8或者Unicode字符集等等。

值得注意的是utf8中,一个汉字3个字节。Unicode一个汉字2个字节。

查看字符编码(UTF-8) (mytju.com)

如何分辨是 文本文件 还是 二进制文件呢?  

最简单的办法,就是把文件拖到记事本上:看下图,像png这种的一拖到记事本,就是乱码,所以它是二进制文件。那么剩下的就是文本文件了

文件路径 

我们既然知道了文件的分类,如何找到这个文件呢?那就需要 文件路径 

 文件路径分为 绝对路径 相对路径 

绝对路径:从最早的根目录开始(就是C盘E盘那些),一直到达这个结点

相对路径:可以从任意目录开始(相对这个目录),一直到这个结点

如下,如果要找test.txt这个文件,绝对路径:E:\code\J20240926-FileIO\test.txt

相对路径需要看从哪里出发(.是当前目录        ..是上一级目录)

若从src里面出发:..\test.txt

若从这个界面的路径出发:.\test.txt

二、文件系统操作

我们文件有个类叫File,我们通常是通过这个类来操作文件。

属性

修饰符及类型属性说明
static StringpathSeparator依赖于系统的路径分隔符,String 类型的表⽰
static charpathSeparator依赖于系统的路径分隔符,char 类 型的表⽰

构造⽅法

签名说明
File(File parent, String child)根据⽗⽬录 + 孩⼦⽂件路径,创建⼀个新的 File 实例
File(String pathname)根据⽂件路径创建⼀个新的 File 实例,路径可以是绝 对路径或者相对路径
File(String parent, String child)根据⽗⽬录 + 孩⼦⽂件路径,创建⼀个新的 File 实 例,⽗⽬录⽤路径表⽰

 方法

修饰符及返回值类型⽅法签名说明
StringgetParent()返回 File 对象的⽗⽬录⽂件路径
StringgetParent()返回 FIle 对象的纯⽂件名称
StringgetPath()返回 File 对象的⽂件路径
StringgetAbsolutePath()返回 File 对象的绝对路径
StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径
booleanexists()判断 File 对象描述的⽂件是否真实 存在
booleanisDirectory()判断 File 对象代表的⽂件是否是⼀ 个目录
booleanisFile()判断 File 对象代表的⽂件是否是⼀ 个文件
booleancreateNewFile()根据 File 对象,⾃动创建⼀个空⽂ 件。成功创建后返回 true
booleandelete()根据 File 对象,删除该⽂件。成功 删除后返回 true
voiddeleteOnExit()根据 File 对象,标注⽂件将被删除,删除动作会到 JVM 运⾏结束时 才会进⾏
String[]list()返回 File 对象代表的⽬录下的所有 ⽂件名
File[]listFiles()返回 File 对象代表的⽬录下的所有 ⽂件,以 File 对象表⽰
booleanmkdir()创建 File 对象代表的⽬录
booleanmkdirs()创建 File 对象代表的⽬录,如果必 要,会创建中间⽬录
booleanrenameTo(File dest)进⾏⽂件改名,也可以视为我们平 时的剪切、粘贴操作
booleancanRead()判断⽤⼾是否对⽂件有可读权限
booleancanWrite()判断⽤⼾是否对⽂件有可写权限

 代码如下:

public class demo1 {
    public static void main(String[] args) throws IOException {
        File file=new File("./test.txt");
        System.out.println(file.getParent());
        System.out.println(file.getName());
        System.out.println(file.getPath());
        System.out.println(file.getAbsoluteFile());
        System.out.println(file.getCanonicalPath());

    }
}

//执行结果
.
test.txt
.\test.txt
E:\code\J20240926-FileIO\.\test.txt
E:\code\J20240926-FileIO\test.txt
public class demo2 {
    public static void main(String[] args) throws IOException {
        File file=new File("./hello");
        System.out.println(file.exists());
        System.out.println(file.isFile());
        System.out.println(file.isDirectory());
        System.out.println(file.delete());
        System.out.println(file.createNewFile());
    }
}

true
false
true
false
false
public class demo3 {
    public static void main(String[] args) {
        File file=new File("./hello/ll/qq");
        System.out.println(Arrays.toString(file.list()));
        System.out.println(file.mkdirs());
    }
}

[]
false
public class demo4 {
    public static void main(String[] args) {
        File srcFile =new File("./test.txt");
        File descFIle =new File("newTest.txt");
        srcFile.renameTo(descFIle);
    }
}

 如何打印目录中的文件呢?

使用递归,把看看是不是目录,里面有没有东西,有的话直接装到一个File[ ]中,然后一个一个打印,遇到目录就继续递归。

public class demo5 {
    private static void scan(File f) throws IOException {
        //1.判断是不是目录
        if(!f.isDirectory()){
            return;
        }
        //2.判断目录内容
        File[] files=f.listFiles();
        if(files==null||files.length==0){
            return;
        }
        //3.打印当前目录
        System.out.println(f.getCanonicalPath());
        //4.打印文件
        for(File file:files){
            if(file.isFile()){
                System.out.println(file.getCanonicalPath());
            }else{
                scan(file);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        File file=new File("./");
        scan(file);
    }
}

四、文件内容操作 

文件内容操作其实很简单!只需要三步:打开写或读关闭

创建了字节流字符流,就相当于打开了文件,用完之后一定要进行关闭。

为什么操作完文件内容之后,要关闭文件呢?

打开文件,其实是在该进程的文件描述表中,创建了一个新的表项。

文件描述符表:描述了该进程都要操作哪些文件,它可以认为是一个数组,数组的每个元素就是一个结构体文件对象(linux中)每个结构体就描述了对应操作的文件的信息,数组的下标被称为“文件描述符”。

每次打开一个文件,就相当于在数组上,占用的一个位置,而在系统内核中,文件描述符表数组,是固定长度 & 不可扩容的。用一个少一个,如果没了就打开不了文件了。

代码如下:

(推荐使用第二种的语法糖,用起来方便简单吃起来很甜。如果多个就用分号隔开,InputStream下面详讲) 

public class demo7 {
    //使用finally
    public static void main1(String[] args) throws IOException {
        InputStream file=null;
        try {
            file=new FileInputStream("./test.txt");
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                file.close();
            }catch (IOException e){
                throw new RuntimeException();
            }
        }
    }
//第二种使用try with resources,会自动帮你关闭
    public static void main(String[] args) throws IOException {
        try(InputStream inputStream=new FileInputStream("./test.txt")) {
            //....
        }
    }
}

而进行写和读,则需要用到字节字符流的一些方法了。 

字节流字符流 

数据流就是计算机对数据的 读取 和 写入 。

为什么叫流呢?

这其实是一个很生动形象的比喻,就像我们接水流100ml,我们可以拿个杯子一次接1ml,接100次,也可以接2ml,接50次.....也可以接100ml,接1次。

所以我们计算机的,可以一次读1个字节,读100次,也可以读2个字节,读50次...也可以读100个字节,读一次。(字符流的话就是读字符)

可以分为两大类:

1)字节流(二进制文件)

 InputStream概述

方法

修饰符及返回值类型⽅法签名说明
intread()读取⼀个字节的数据,返回 -1 代表 已经完全读完了
intread(byte[] b)最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1 代表 以及读完了
intread(byte[] b, int off, int len)最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读 到的数量;-1 代表以及读完了
voidclose()关闭字节流

InputStream 只是⼀个抽象类,要使⽤还需要具体的实现类。关于 InputStream 的实现类有很多,基 本可以认为不同的输⼊设备都可以对应⼀个 InputStream 类,我们现在只关⼼从⽂件中读取,所以使⽤ FileInputStream

 FileInputStream 概述

构造方法

签名说明
FileInputStream(File file)利⽤ File 构造⽂件输⼊流
FileInputStream(String name)利⽤⽂件路径构造⽂件输⼊流
public class demo8 {
    public static void main(String[] args) throws IOException {
        try (InputStream inputStream = new FileInputStream("./test.txt")) {
            while (true) {
                byte[] buffer = new byte[1024];
                inputStream.read();
                int n = inputStream.read(buffer);

                if (n == -1) {
                    break;
                }
                for (int i=0;i<n;i++){
                    System.out.printf("0x%x\n",buffer[i]);
                }
            }
        }
    }
}

这里的buffer是输出型参数,就是被当参数传进去,然后被填满着出来。

就像你去食堂吃饭,把空的盘子给阿姨,阿姨给你抖抖抖,装到你盘子里,然后还给你。

利⽤ Scanner 进⾏字符读取 

上述例⼦中,我们看到了对字符类型直接使⽤ InputStream 进⾏读取是⾮常⿇烦且困难的,所以,我 们使⽤⼀种我们之前⽐较熟悉的类来完成该⼯作,就是 Scanner 类。 

构造方法说明
Scanner(InputStream is, String charset)使⽤ charset 字符集进⾏ is 的扫描读取
// 需要先在项⽬⽬录下准备好⼀个 hello.txt 的⽂件,⾥⾯填充 "你好中国" 的内容
public class Main {
     public static void main(String[] args) throws IOException {
         try (InputStream is = new FileInputStream("hello.txt")) {
             try (Scanner scanner = new Scanner(is, "UTF-8")) {
                 while (scanner.hasNext()) {
                     String s = scanner.next();
                     System.out.print(s);
                 }
             }
         }
     }
}
OutputStream 概述 

⽅法 

修饰符及返回值类型方法签名说明
voidwrite(int b)写⼊要给字节的数据
voidwrite(byte[] b)将 b 这个字符数组中的数据全部写 ⼊ os 中
intwrite(byte[] b, int off, int len)将 b 这个字符数组中从 off 开始的 数据写⼊ os 中,⼀共写 len 个
voidclose()关闭字节流
voidflush()重要:我们知道 I/O 的速度是很慢 的,所以,⼤多的 OutputStream 为了减少设备操作的次数,在写数 据的时候都会将数据先暂时写⼊内 存的⼀个指定区域⾥,直到该区域 满了或者其他指定条件时才真正将 数据写⼊设备中,这个区域⼀般称 为缓冲区。但造成⼀个结果,就是 我们写的数据,很可能会遗留⼀部 分在缓冲区中。需要在最后或者合 适的位置,调⽤ flush(刷新)操 作,将数据刷到设备中。

 OutputStream 同样只是⼀个抽象类,要使⽤还需要具体的实现类。我们现在还是只关⼼写⼊⽂件 中,所以使⽤ FileOutputStream

public class demo9 {
    public static void main(String[] args) throws IOException {
        try(OutputStream outputStream=new FileOutputStream("./test.txt",true)) {

//            outputStream.write(98);

            byte[] bytes=new byte[]{98,98,97};
            outputStream.write(bytes);
        }
    }
}

 注意:!!!

这里的OutPutStream它写入的时候,会覆盖文件已有的内容,如果要接着文件内容写,要在FileOutputStream的参数那里,加一个true。

2)字符流(文本文件)

为什么既然都能用,为什么还需要字符流呢?

因为用字节流时,需要我们程序员去记住那些字节对应的东西,如果是中文的话,就需要3个字节,对程序员来说是一件比较麻烦的事情,所以我们直接读字符,也就是字符流。

同理,一个是Reader,一个是Writer,也都是抽象类不能直接实例,而是通过FileReader和FileWrite来实例。 

Reader方法如下: 

 

public class demo10 {
    public static void main(String[] args) {
        try (Reader reader = new FileReader("./test.txt")) {
//            int n=reader.read();
//            char ch=(char)n;
//            System.out.println(ch);
            while (true) {
                char[] chs = new char[1024];
                int n=reader.read(chs);
                if(n==-1){
                    break;
                }
                for(int i=0;i<n;i++){
                System.out.print(chs[i]);}
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 Writer方法如下:

public class demo11 {
    public static void main(String[] args) {
        try(Writer write=new FileWriter("./test.txt",true)) {
            write.write("你好世界");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

五、实现文件查找,删除,复制

根据文件内容查找:

首先先输入它所在的目录,然后看看这个目录有没有错。

没错的话,就输入文件内容的关键字,然后scan搜索递归, 不是目录就返回,把它放到一个File[]里面,空的也返回。

然后reader一下把文件的内容都读到字符串里面,看看有没有包含关键字key。没有的话就下一个,是目录就继续递归。

public class demo14 {
    public static void main(String[] args) throws IOException {
        Scanner scanne = new Scanner(System.in);
        System.out.println("请输入你要搜索的路径");
        String rootPath = scanne.next();
        File file = new File(rootPath);
        if (!file.isDirectory()) {
            System.out.println("路径有误");
            return;
        }
        System.out.println("请输入关键字");
        String key = scanne.next();
        scan(file, key);

    }

    private static void scan(File file, String key) throws IOException {
        if (!file.isDirectory()) {
            return;
        }
        File[] files = file.listFiles();
        if (files == null || files.length == 0) {
            return;
        }
        for (File f : files) {
            if (f.isFile()) {
                doSreach(f, key);
            } else {
                scan(f, key);
            }
        }
    }

    private static void doSreach(File file, String key) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        try (Reader reader = new FileReader(file)) {
            while (true) {
                char[] buffer = new char[1024];
                int n = reader.read(buffer);
                if (n == -1) {
                    break;
                }
                stringBuilder.append(buffer,0,n);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(stringBuilder.indexOf(key)==-1){
            return;
        }else{
            System.out.println("找到了"+file.getCanonicalPath());
        }
    }
}

删除:

首先也是输入文件所在的目录, 然后看看这个目录有没有错。

没错的话,就输入文件名字的关键字,然后scan搜索递归, 不是目录就返回,把它放到一个File[]里面,空的也返回。

然后按File[]的内容,for循环找有这个关键字名字的文件,提示用户要不要删除。

public class demo12 {
    public static void main(String[] args) throws IOException {
        System.out.println("请输入你要搜索的路径");
        Scanner scanner=new Scanner(System.in);
        String rootPath=scanner.next();
        File file=new File(rootPath);
        if(!file.isDirectory()){
            System.out.println("路径不存在");
            return;
        }
        System.out.println("请输入你要删除文件的关键字");
        String key=scanner.next();
        scan(file,key);
    }

    private static void scan(File currentDir, String key) throws IOException {
        if(!currentDir.isDirectory()){
            return;
        }
        File[] files=currentDir.listFiles();
        if(files==null||files.length==0){
            return;
        }
        for(int i=0;i<files.length;i++){
            if(files[i].isFile()){
                doDelete(files[i],key);
            }else {
                scan(files[i],key);
            }
        }
    }

    private static void doDelete(File file, String key) throws IOException {
        if(!file.getName().contains(key)){
            return;
        }
        Scanner scanner=new Scanner(System.in);
        System.out.println(file.getCanonicalPath()+"是否要删除该文件,Y/n");
        String s=scanner.next();
        if(s.equals("Y")||s.equals("y")){
            file.delete();
        }
    }
}

复制:

首先也是输入文件所在的路径, 然后看看这个文件有没有错。

没错的话,就输入你要复制到目标地的路径,如果这时候目标路径的父节点不是目录就不行,因为这个复制是要跟目的地同一个等级。

然后源文件和目的地都没错之后,就可以从源文件中读,然后在目的地中写。

public class demo13 {
    public static void main(String[] args) {
        System.out.println("请输入源文件路径");
        Scanner scanner = new Scanner(System.in);
        String srcPath = scanner.next();
        File srcFile = new File(srcPath);
        if (!srcFile.isFile()) {
            System.out.println("无此文件");
            return;
        }
        System.out.println("请输入目标文件路径");
        String descPath = scanner.next();
        File descFile = new File(descPath);
        if (!descFile.getParentFile().isDirectory()) {
            System.out.println("路径有误");
            return;
        }
        try (InputStream inputStream = new FileInputStream(srcFile);
             OutputStream outputStream = new FileOutputStream(descFile)) {
            while (true) {
                byte[] buffer = new byte[1024];
                int n = inputStream.read(buffer);
                if (n == -1) {
                    break;
                }
                outputStream.write(buffer,0,n);

            }
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

IO拒绝访问 

(在输入目的路径的时候可能会“拒绝访问”报错)

这是因为你没有指定你复制到目的地的名字,只给了路径,还需要给它安一个名字。

如上图,就不会报错了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值