Java中文件操作和IO

文件操作和IO

文件的基本概念

狭义上问文件,针对硬盘这种I/O设备的存储

随着文件变多,为了更好的管理,就按照层级结构对文件进行组织 (也就是树形结构这种),这样就有了文件夹 或者目录 这种概念

一个文件夹中可以有多个文件,并且文件夹中也可以有文件夹,就这样不断层层嵌套,这样的层级结构

文件路径(Path)

分为绝对路径和相对路径

绝对路径 :从树型结构的⻆度来看,树中的每个结点都可以被⼀条从根开始,到达文件终点
相对路径:可以从任意节点,进行路径描述

这里再Windows中使用 \ 反斜杠进行分割,其也可以使用 / 斜杠进行分割,但反斜杠 \ 这种方式在代码中需要进行字符进行转义才可以使用,因此使用 / 斜杠最好

文件类型

文本文件和二进制文件

文本文件 :内部存储的内容是"字符串",每一个部分都是一个"字符"(字符集/字符编码)
二进制文件 :没有限制,可以是任何数据

简单方法看文件是什么编码

直接使用记事本打开,如果是乱码,就是二进制文件,不乱码就是文本文件

File

属性

类型 属性 说明
static String pathSeparator 依赖于系统的路径分隔符,String类型的表⽰
staticchar pathSeparator 依赖于系统的路径分隔符,char类型的表⽰

构造方法

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

此时这里的File(String pathname)根据当前目录,其如果是相对目录的话 就是想对当前idea打开文件的路径

方法


java 复制代码
public class demo2 {
    public static void main(String[] args) throws IOException {
        File file = new File("d:/JAVA/Test/test.txt");
       // File file = new File("./hello.txt");//这样就会在java当前文件中找是否有这个文件
        System.out.println(file.getParent());//父目录
        System.out.println(file.getName());//文件名
        System.out.println(file.getPath());//文件位置
        System.out.println(file.getAbsoluteFile());
        System.out.println(file.getCanonicalFile());
    }
}

此时getAbsoluteFile()和getCanonicalFile()这个方法好像差不多,实则不然

这里getCanonicalFile()获取信息更标准,并且会把中间一些不必要的删除

java 复制代码
public class demo3 {
    public static void main(String[] args) {
        File file = new File("d:/JAVA/Test/hello.txt");
        System.out.println(file.exists());//是否存在
        System.out.println(file.isFile());//是否是一个普通文件
        System.out.println(file.isDirectory());//是否是一个目录
    }
}

此时这个文件是一个普通文件,不是目录,文件存在

java 复制代码
file.delete();//删除文件,直接从硬盘上删除
java 复制代码
public class demo3 {
    public static void main(String[] args) {
        File file = new File("d:/JAVA/Test/hello.txt");
        System.out.println(file.exists());
        //删除文件
        file.delete();
        System.out.println(file.exists());
    }
}


java 复制代码
file.deleteOnExit();//也是删除,JVM退出之前才进行删除
java 复制代码
public class demo4 {
    public static void main(String[] args) {
        File file = new File("d:/JAVA/Test/test.txt");
        System.out.println(file.exists());
        file.deleteOnExit();//程序结束前删除
        System.out.println(file.exists());

        //这里输入会让这个删除滞后
        Scanner sc = new Scanner(System.in);
        sc.nextInt();
    }
}

这里输入会阻塞主线程,所以当我输入输入未完成 的时候,其文件还处于未删除状态 ,当输入完成 ,后面没有什么逻辑,其就会开始删除了

java 复制代码
 String[] fileNames = file.list();//返回file目录下所以文件名
 并用String[]接收返回值
java 复制代码
public class demo5 {
    public static void main(String[] args) throws IOException {
        File file = new File("d:/Java/python");
        String[] fileNames = file.list();
        if(fileNames == null){
            System.out.println("空目录");
            return;
        }
        for(String fileName : fileNames){
            System.out.println(fileName);
        }
     }
}
java 复制代码
File[] files = file.listFiles();
//此时返回file目录下所有文件,每一个文件用File表示
java 复制代码
public class demo5 {
    public static void main(String[] args) throws IOException {
        File file = new File("d:/Java/python");
        File[] files = file.listFiles();
        if(files == null){
            System.out.println("空目录");
            return;
        }
        for (File f : files){
            System.out.println(file.getCanonicalFile());
        }
    }
}
java 复制代码
file.mkdirs();//只可以创建一个当前目录
file.mkdirs();//如果创建过程中需要中间目录也会进行创建
返回值都是boolean
java 复制代码
public class demo6 {
    public static void main(String[] args) {
        File file = new File("d:/Java/Test/aaa");
        System.out.println(file.isDirectory());//判断这个目录是否存在
        file.mkdir();//创建file对象的目录
        System.out.println(file.isDirectory());
    }
}
java 复制代码
public class demo6 {
    public static void main(String[] args) {
        File file = new File("d:/Java/Test/aaa/bbb/ccc");
        System.out.println(file.isDirectory());//判断这个目录是否存在
        file.mkdirs();//创建file对象的目录,如果需要创建中间目录,也会创建
        System.out.println(file.isDirectory());
    }
}
java 复制代码
renameTo(File dest);
//对文件进行改名,相当于文件的剪切和粘贴操作
//如果此时的目录是./aaa/bbb/ccc,这里对aaa文件夹进行改名是修改不了
java 复制代码
public class demo7 {
    public static void main(String[] args) {
        File source = new File("d:/Java/Test/aaa");
        File dest = new File("d:/Java/Test/mmm");
        source.renameTo(dest);
    }
}

文件读写-数据流(stream)

读:就是把文件内容数据读取出来

写:就是向文件夹中写数据

分为两个大类

1.字节流:读写以字节为单位,像这里InputStream输入,OutputStream输出

2.字符流:读写以字符为单位,像这里Reader输入,Writer输出

InputStream

java 复制代码
read()读取一个字节的数据,返回-1表示读完了
read(byte[] b),b是"输出型参数"
read(byte[] b,int off, int len)//从off偏移量开始读取,读len个字节
-1表示的是读到了文件末尾
注意:这里返回值都是int,因为"字节对齐"的概念,因为计算机读取4/8个字节
的效率大于读取1/2个字节

InputStream是一个抽象类,因此不可以直接实例化对象,可以实例化FileInputStream对象

这里可能有FileNotFoundException异常,这个异常是IOException的子类

java 复制代码
public class demo8 {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = new FileInputStream("D:/JAVA/Test/test.txt");
        while (true){
            int n = inputStream.read();
            if(n == -1){
                break;//文件读取完毕
            }
            System.out.printf("%x",n);
            System.out.println();
        }

    }
}


上面这个是会不断的读取硬盘,每一个字节都会触发一次read()读取,并且读取硬盘也比较慢,因此我们需要对其进行优化,并且还没有使用close进行文件关闭

关闭文件close()

在操作系统中有进程,进程中有个PCB里面有文件描述符 表,每次打开文件,都会在这里进行记录,但是这个文件描述附表是不能自动扩容,因此如果打开文件过多,就放不下了,必须关闭文件其才会将其资源释放出来

java 复制代码
public class demo8 {
    public static void main(String[] args){
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream("D:/JAVA/Test/test.txt");
            while (true){
                //将这些数据放入1024数组中,可能超出
                //因此这里采用存储,一次可以读这么多的数据
                byte[] bytes = new byte[1024];
                int n = 0;

                n = inputStream.read(bytes);

                if(n == -1){
                    break;
                }
                for (int i = 0; i < n; i++) {
                    System.out.printf("%x\n",bytes[i]);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            //进行资源释放
            if(inputStream != null){
                try{
                    inputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
java 复制代码
上面这个代码中try catch中又嵌套try catch,这样的代码十分的丑陋,因此
try with resources
java 复制代码
public class demo8 {
    public static void main(String[] args){
        //上面还是try with resources语法解决上面问题
        try (InputStream inputStream = new FileInputStream("D:/JAVA/Test/test.txt");){
            while (true){
                byte[] bytes = new byte[1024];
                int n = inputStream.read(bytes);
                if(n == -1){
                    break;
                }
                for (int i = 0; i < n; i++) {
                    System.out.printf("%x\n",bytes[i]);
                }

            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

并且这里会自动调用close

Scanner对字符读取

上面读取字符还是以byte单位进行读取,因此可以使用Scanner

java 复制代码
public class demo12 {
    public static void main(String[] args) {
        try (InputStream inputStream = new FileInputStream("d:/Java/Test/test.txt")) {

            Scanner scanner = new Scanner(inputStream,"UTF-8");
            String s = scanner.next();
            System.out.println(s);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

OutputStream


这里的OutputStream 仍然是一个抽象类,因此需要实例化FileOutputStream对象

java 复制代码
public class demo9 {
    public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("d:/Java/Test/test.txt")){
            outputStream.write(65);
            outputStream.write(66);
            outputStream.write(67);
            outputStream.write(68);

        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

此时对这个文件进行写入

java 复制代码
使用byte[]数组写入
java 复制代码
public class demo9 {
    public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("d:/Java/Test/test.txt")){
//            outputStream.write(65);
//            outputStream.write(66);
//            outputStream.write(67);
//            outputStream.write(68);
            byte[] bytes = {68,69,70,71};
            outputStream.write(bytes);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

此时法现一个问题,就是前面写的ABCD好像被这次写的覆盖了

这里默认是覆盖写

java 复制代码
//将这个写入变成追加写
OutputStream outputStream = new FileOutputStream("d:/Java/Test/test.txt",true)
//将这个参数设置成true其就会变成追加写
java 复制代码
public class demo9 {
    public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("d:/Java/Test/test.txt",true)){

            byte[] bytes = {'h','e','l','l','o'};
            outputStream.write(bytes);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

写之前

写之后

PrintWriter

这个类提供了print/println/printf进行文件的写入操作

java 复制代码
public class demo13 {
    public static void main(String[] args) {
        try(PrintWriter writer = new PrintWriter("d:/Java/Test/test.txt")) {
            writer.printf("%d + %d = %d" , 10,20,30);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

Reader和Writer

Reader是一个抽象类,因此不可以直接实例化对象,但是可以实例化FileReader对象


java 复制代码
public class demo10 {
    public static void main(String[] args) {
        try(Reader reader = new FileReader("d:/Java/Test/test.txt")) {
            while (true){
                int n = reader.read();
                if(n == -1){
                    break;
                }

//                System.out.print(n);
                System.out.println((char) n);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

上面这样重复读取,一次读取一个字符有些浪费时间,因此这里可以每次使用一个char[]进行接收1024个字符,就这样,循环读取,直到读取完

java 复制代码
public class demo10 {
    public static void main(String[] args) {
        try(Reader reader = new FileReader("d:/Java/Test/test.txt")) {
            //上面这样会重复读取,因此我们可以使用一个char[]数组一次接收1024个字符
            while (true){
                char[] chars = new char[1024];
                int n = reader.read(chars);//将读取到的信息放入chars数组中
                if(n == -1){
                    break;
                }
                for (int i = 0; i < n; i++) {
                    System.out.println(chars[i]);
                }
            }
            }catch (IOException e){
            e.printStackTrace();
        }
    }
}


java 复制代码
//Writer是一个抽象类
Writer writer = new FileWriter()
java 复制代码
public class demo11 {
    public static void main(String[] args) {
        try(Writer writer = new FileWriter("d:/Java/Test/test.txt")){
            writer.write("hello world");
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

实例练习

示例一

扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否要删除该⽂件

java 复制代码
1.先输入目录和关键字
2.读取内容,获取路径,并进行删除
java 复制代码
//根据关键字,当前目录下找出所有相关的文件
public class demo14 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入你要搜索的根目录");
        String rootPath =  sc.next();

        File rootfile = new File(rootPath);
        if(!rootfile.isDirectory()){
            System.out.println("输入目录不存在或者不是目录");
            return;
        }

        System.out.println("请输入你要找的关键字");
        String key = sc.next();

        reserch(rootfile,key);
    }

    //找文件
    private static void reserch(File rootfile, String key) {
        //列出文件所有内容
        File[] files = rootfile.listFiles();
        if(files == null){
            System.out.println("文件目录下为空");
            return;
        }
        for(File file : files){
            //判断是否是文件
            if(file.isFile()){
                //判断是否包含关键字
                Delete(file,key);
            }else if(file.isDirectory()){
                //是目录文件,需要继续搜索
                reserch(file,key);
            }else{

            }
        }
    }

    //删除操作
    private static void Delete(File file, String key) {
        Scanner sc = new Scanner(System.in);
        //判断是否包含关键字
        if(file.getName().contains(key)){
            System.out.println("找到文件"+file.getAbsolutePath());
            System.out.println("是否删除:Y/N");
            String choice = sc.next();
            //进行删除
            if(choice.equals("Y")||choice.equals("y")){
                file.delete();
            }else{
                System.out.println("取消删除");
            }
        }
    }
}


删除成功

示例二

进⾏普通⽂件的复制

java 复制代码
1.输入源文件路径和目标目录(判断是否存在)
2.进行源文件读和目标文件的写(目标文件会自动创建,因此目标文件只要父目录存在即可)
java 复制代码
//将一个文件的内容复制到另一个文件中
public class demo15 {
    public static void main(String[] args) {
        //1.输入复制和被复制的文件目录
        Scanner sc = new Scanner(System.in);
        System.out.println("输入源文件路径");
        String src = sc.next();
        System.out.println("输入目标文件路径");
        String dest = sc.next();

        //判断sc这个路径是否合法
        File srcFile = new File(src);
        if(!srcFile.isFile()){
            System.out.println("源文件路径不合法");
            return;
        }
        //判断目标文件的parent是否存在
        File destFile = new File(dest);
        if(!destFile.getParentFile().isDirectory()){
            System.out.println("目标文件不合法");
            return;
        }

        //打开src和dest文件,对dest进行写入
        try(InputStream inputStream = new FileInputStream(srcFile);
            OutputStream outputStream = new FileOutputStream(destFile)) {
            while (true) {
                byte[] bytes = new byte[1024];
                int n = inputStream.read(bytes);
                if (n == -1) {
                    break;
                }
                outputStream.write(bytes, 0, n);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

示例三

扫描指定⽬录,并找到名称或者内容中包含指定字符的所有普通⽂件(不包含⽬录)

这里和示例一区别就是也需要对文件内容进行判断是否包含关键字

java 复制代码
public class demo16 {
    public static void main(String[] args) {
        //1.输入根目录和关键字
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入根目录");
        String rootPath = sc.next();
        System.out.println("请输入关键字");
        String key = sc.next();

        //判断key关键字是否合法
        if(key.isEmpty()){
            System.out.println("关键字不合法");
            return;
        }
        File rootFile = new File(rootPath);
        if(!rootFile.isDirectory()){
            //不是路径
            System.out.println("根目录有误");
            return;
        }

        search(rootFile,key);
    }
    public static void search(File rootFile,String key){
        //列出所有
        File[] files = rootFile.listFiles();
        if(files == null){
            System.out.println("没有文件");
            return;
        }
        for(File file : files){
            if(file.isFile()){
                //进行文件名及其内容进行判断是否有关key关键字
                trySearch(file,key);
            }else if(file.isDirectory()){
                //如果是路径,需要继续进行找
                search(file,key);
            }
        }
    }

    private static void trySearch(File file, String key) {
        //1.先看文件名是否包含
        if(file.getName().contains(key)){
            System.out.println("找到了!通过文件名"+file.getAbsolutePath());
        }
        //这里判断内容中是否有关于关键字的,此时文件内容放一起进行判断内容是否有关于这个关键字
        //使用Reader这样可以一个字符,一个字符的读取
        try(Reader reader = new FileReader(file)) {
            StringBuffer result = new StringBuffer();
            while (true){
                char[] chars = new char[1024];
                int n = reader.read(chars);
                if(n == -1){
                    break;
                }
                //将真实字符数量拼接一起
                result.append(chars,0,n);
            }
            if(result.indexOf(key) >= 0){
                //找到了
                System.out.println("找到了!内容匹配" + file.getAbsolutePath());
            }else {
                return;
            }

        }catch (IOException e){
            e.printStackTrace();
        }
    }
}



相关推荐
夏天的味道٥2 小时前
IDEA 开发工具常用插件整理
java·ide·intellij-idea
勇者无畏4042 小时前
基于 Spring AI Alibaba 搭建 Text-To-SQL 智能系统(初始化)
java·后端·spring
一枚懒人2 小时前
Java的Lamdba语法和函数式编程理解
java
土豆南瓜饼2 小时前
关于mybatis-plus的一些默认配置
java
Juchecar2 小时前
Java示例:设计模式是如何在实战中“自然生长”出来
java·设计模式
能摆一天是一天2 小时前
JAVA Function
java
The Sheep 20232 小时前
Dotnet-Dapper的用法
java·开发语言
SimonKing2 小时前
百度统计、Google Analytics平替开源网站分析工具:Umami
java·后端·程序员
Juchecar2 小时前
设计模式不是Java专属,其他语言的使用方法
java·python·设计模式