JavaEE--文件IO

一、内存与外存

我们平时谈到的“文件”,指的都是硬盘上的文件,硬盘(外存)和内存相比:

速度:内存比硬盘快很多
空间:内存空间比硬盘小
成本:内存比硬盘贵
持久化:内存掉电后数据丢手,外村掉电后数据还在。

文件IO这里也是操作硬盘。
(1)文件系统操作:创建文件,删除文件,重命名文件,创建目录……
(2)文件内容操作:针对文件内容进行读和写。

二、路径

1、文件路径

路径是文件系统上一个文件/目录的具体位置。这里所说的目录就是通俗易懂的文件夹的专业说法。
计算机的目录是有层级结构的
在这里插入图片描述
可以看出文件系统是以树型结构来组织文件和目录的(N叉树)。

2、绝对路径和相对路径

文件路径就是从根节点出发,沿着树杈,一路往下走,到达目标文件,表示路径的时候,可以把“此电脑”省略,直接从盘符开始表示。如图:
在这里插入图片描述
实际表示路径,是通过一个字符串表示,每个目录之间使用/或者\来分割。我们点上去
在这里插入图片描述
这里反斜杠\只是在windows中适用,代码中写需要携程\,需要转义字符的。

绝对路径:从盘符开始,一层一层往下找,这个过程,得到的路径就称作绝对路径。
相对路径:从给定的某个目录出发,一层一层往下找,这个或称得到的路径就称作相对路径。
总之,一定要明确,基准目录(工作目录)是啥

1、假设工作路径是这个:
在这里插入图片描述
此时要找到在这里插入图片描述
相对路径表示:./4.JavaEE初阶/2.多线程(基础).pdf
2、假设工作路径是这个:
在这里插入图片描述
此时要找到
在这里插入图片描述
相对路径表示:./JavaEE课件/4.JavaEE初阶/2.多线程(基础).pdf

绝对路径都一样,可以理解成以“此电脑”为工作路径;相对路径要看基准目录是啥,基准目录不一样,结果就比一样。
相对路径中 . 是一个特殊符号,表示当前目录
… 也是特殊符号,表示当前目录的上级目录

3、总结

(1)文件系统上,任何一个文件,对应的路径是唯一的,不会存在两个路径相同,但是文件不同的情况。
(2)在Linux,可能存在一个文件,有两个不同的路径能找到它,在Windows上不存在。
(3)Windows上可以认为,路径和文件是一一对应的,路径相当于一个文件的“身份标识”。

三、文本文件和二进制文件

1、概念区分

文本文件存储的是文本,文本文件的内容都是由ascci字符构成的。
在这里插入图片描述
对于ascci来说,表示范围0~127,后来,又搞了一些其他的编码方式,utf8之类的,就可以针对其他的语言文字符号进行编码了。而utf8就可以想象成一个更大的表。(但是终究还是有限的)
总结:

文本文件里存储的数据,就是遵守ascci或者其他字符集编码,所得到的文件,本质上存的是字符(不仅仅是所说的char)

二进制文件存储的是二进制数据。

二进制文件没有任何字符集的限制(存啥都行)

2、记事本方法

直接使用记事本打开某个文件,看不懂就是二进制文件,能看明白就是文本文件。(记事本是按照文本的方式来解析显示的,解析出来就是文本文件,解析不出来就是二进制文件)
类似.txt, .Java,.C……是二进制文件。.class,.exe,jpg,mp3都是二进制文件。
事实上,二进制都是一个一个的字节,记事本尝试把当前若干个自己的数据往utf8码表里套,套出来的是啥就是啥,掏不出来的就是方块。

四、文件系统操作

Java标准库,提供了File这个类,File对象是硬盘上的一个文件的“抽象“表示。

文件是存储在硬盘上的,直接通过代码操作硬盘,不太方便。于是咱们就在内存中创建一个对应的对象,操作这个内存中的对象,就可以间接的影响到硬盘的文件情况了。

构造File对象

绝对路径

import java.io.File;

public class IODemo1 {
    public static void main(String[] args) {
        File file = new File("d:/cat.jpg");
        // 就可以通过File对象进行操作了
    }
}

构造过程中,可以使用绝对路径/相对路径进行初始化,这个路径指向的文件,可以真实存在的,也可以是不存在的:
在这里插入图片描述
这里的i 就是input(输入),o 就是output(输出)。与此同时File提供了一些方法:
在这里插入图片描述

相对路径

然后我们进行代码修改,将他修改成相对路径:

import java.io.File;
import java.io.IOException;

public class IODemo1 {
    public static void main(String[] args) throws IOException {
        File file = new File("./cat.jpg"); // 改成相对路径
        // File file = new File("d:/cat.jpg");// 绝对路径
        // 就可以通过File对象进行操作了
        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());
    }
}

我们看运行结果有什么变化:
在这里插入图片描述
最后一个getCanonicalPath是对上面AbsolutePath的一种优化(少了一个 . )实际上是一样的。了解即可,日常开发中使用绝对路径是完全够用的。

创建文件

import java.io.File;
import java.io.IOException;

public class IODemo2 {
    public static void main(String[] args) throws IOException {
        // 在相对路径中, ./ 通常是可以省略的
        File file = new File("./hello_world.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());
    }
}

以上代码我们创建了一个hello_world的一个txt文件。运行后结果为
在这里插入图片描述
有结果可知在进行createFile创建文件之前,运行结果都是flae,创建后文件存在。并且我们在可以发现这里多了一个hello_world.txt的一个文件:
在这里插入图片描述
我们打开它:
在这里插入图片描述
在这里插入图片描述
可以发现这个文件创建在了System_code这个目录下。

删除文件

在这里插入图片描述

创建目录

import java.io.File;

public class IODemo3 {
    public static void main(String[] args) {
        // 只能创建一级目录
       /* File file = new File("test-dir");
        file.mkdir();*/
        // 创建多级目录
        File file1 = new File("test-dir/aa/bb");
        file1.mkdirs();
    }
}

罗列目录内容

import java.io.File;
import java.lang.reflect.Array;
import java.util.Arrays;

public class IODemo4 {
    // 罗列目录内容
    public static void main(String[] args) {
        File file = new File("test-dir");
        String[] results = file.list();
        System.out.println(Arrays.toString(results));

        File[] results2 = file.listFiles();
        System.out.println(Arrays.toString(results2));
    }
}

目录重命名

import java.io.File;

public class IODemo5 {
    public static void main(String[] args) {
        // 重命名
        File src = new File("./test-dir");
        File dest = new File("./test222");
        src.renameTo(dest);
    }
}

五、文件内容操作

一、针对文本文件,提供了一组类,统称为“字符流”(Reader,Writer)。读写的基本单位是 字符。
二、针对二进制文件,提供了一组类,统称为“字节流”(InputStream,OutputStream)读写的基本单位是 字节。

每种流对象,又分为两种:
(1)输入的:Reader(文本),InputStream(二进制)
(2)输出的:Writer(文本),OutputStream(二进制)

在这里插入图片描述

文件打开

InputStream用来进行IO,IO不仅仅是读写硬盘的文件还可以读写别的。InputStream使用方法:
实例化InputStream不能直接创建,我们点开发现:
在这里插入图片描述
它是一个抽象类,不能直接实例化,代码如下:

import java.io.IOException;
import java.io.InputStream;

public class IODemo6 {
    public static void main(String[] args) throws IOException {
        // 对于InputStream这个类点开发现是一个abstract抽象类,抽象类不能实例化,我们实例化它的子类
        //InputStream inputStream = new FileInputStream("d:/test.txt");// 从文件里面读取
        // 这个过程相当于C中的fopen,文件的打开操作

        //inputStream.close();// 关闭操作,单独一个close不安全,于是……

        // 简单优雅的写法:
        try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
            // 读文件
            // read一次返回的是一个字节,但是此处的返回值类型是int
            while (true) {
                int b = inputStream.read();
                if (b == -1) {
                    // 读到末尾,结束循环即可
                    break;
                }
                System.out.println(b);
                // 如果写是汉字,用16进制打印如下:
                // System.out.printf("%x\n", b);
            }
        }
    }
}

在这里插入图片描述
在这里插入图片描述
这里面最终的简化版本带有资源的try操作,会在try代码快结束,自动执行close关闭操作

字节流(I/O)

对于read操作为我们可以发现它有三种方法:
在这里插入图片描述
但是这里不用byte去接收它,一个byte表示的范围是-128~+127,如果文件读取完毕,到头了,再次read就会返回-1。

read和write还可以一次读写多个字节,使用byte[]来表示。
read,会尽可能把byte[]给填满,如果读到末尾,返回-1
write,会把byte[]所有数据都写入文件

字符流操作(R/W)

在这里插入图片描述

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class IODemo8 {
    // 字符流操作
    public static void main(String[] args) {
        try (Reader reader = new FileReader("d:/test.txt")) {
            while (true) {
                int c = reader.read();
                if (c == -1) {
                    break;
                }
                char ch = (char) c;
                System.out.println(ch);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

不管是字节流还是字符流都有read和write操作,就看它怎么读,字节流就以字节为单位,字符流就以字符为单位

写入文件

import java.io.*;

public class IODemo7 {
    public static void main(String[] args) {
        // 写入文件
        try (OutputStream outputStream = new FileOutputStream("d:/test.txt")) {
          // 按照一个字节一个字节写入

            outputStream.write(97);
          outputStream.write(98);
          outputStream.write(99);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

六、文件操作案例

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

这个程序就是找出哪些文件,是包含这个关键词的,这里我们以 hello 这个关键词为例。方法如下:
(1)先去递归遍历目录,比如给定一个d:/去递归的把这里包含的所有文件都列出来。
(2)每次找到一个文件,都打开,并读取文件内容(得到String)。
(3)在判定要查询的词,是否在上述文件内容中存在,如果存在,结果即为所求。
事实上,这种方法并不高效,小规模搜索还是可以的,更高效的实现,需要依赖“倒排索引”这样的数据结构。
代码如下:

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Scanner;

public class IODemo9 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // !、先让用户指定一个要搜索的根目录
        System.out.println("请输入要扫描的根目录:");
        File rootDir = new File(scanner.next());
        if (!rootDir.isDirectory()) {
            System.out.println("输入有误,您输入的目录不存在");
            return;
        }

        // 2、让用户输入一个要查询的词
        System.out.println("请输入要查询的词:");
        String word = scanner.next();

        // 3、递归的进行目录/文件的遍历
        scanDir(rootDir, word);
    }

    private static void scanDir(File rootDir, String word) {
        // 列出当前的rootDir中的内容,没有内容,直接递归结束
        File[] files = rootDir.listFiles();// 列出里面的内同
        if (files == null) {
            // 当前rootDir是一个空的目录,这里什么都没有
            // 因而就没有必要递归
            return;
        }
        // 目录里有内容,就便利目录中的每个元素
        for (File f : files) {
            System.out.println("当前搜素到:" + f.getAbsolutePath());
            if (f.isFile()) {
                // 是普通文件->打开文件,读取内容,比较看是 否包含上述关键词
                String content = readFile(f);
                if (content.contains(word)) {
                    System.out.println(f.getAbsolutePath() + "包含要查找的关键字!");
                }
            } else if (f.isDirectory()) {
                // 是目录->进行递归操作
                scanDir(f, word);// 此处这里的递归,其实就是以当前f这个目录为起点
                                 // 再搜索里面的内容了(类比二叉树遍历)
            } else {
                // 不是普通文件,也不是目录文件,直接跳过(Linux里)
                continue;
            }
        }
    }

    private static String readFile(File f) {
        // 读取文件的整个内容,返回出来。
        // 使用字符流来读取,由于咱们匹配的是字符串,此处只能按照字符流处理才有意义
        StringBuilder stringBuilder = new StringBuilder();
        try (Reader reader = new FileReader(f)) {
            // 一次读一个字符,把读到的结果给拼装到StringBuilder中,统一转成String
            while (true) {
                int c = reader.read();
                if (c == -1) {
                    break;
                }
                stringBuilder.append((char)c);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return stringBuilder.toString();
    }
}

运行结果:
在这里插入图片描述
这里我们要找的关键词是hello,事先创建好的文件夹里就找到了这个关键词。


文件内容以及操作就到此为止,这一期文件的内容学习是为了后期学习网络作铺垫的。下期再见吧
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值