通常说的文件时磁盘上存储的文件,但文件的概念更加广泛,为了操作系统方便管理硬件设备,把一切资源都抽象成文件进行管理「比如操作网卡,显卡,CPU,打印机等设备」

1. File类的使用

1.1 读取文件属性

package file;

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

public class Demo1 {
    public static void main(String[] args) throws IOException {
        // 绝对路径
        File f = new File(" /Users/cxf/java/4.System/src/file/content/test.txt");
        System.out.println(f.getParent());
        System.out.println(f.getName());
        System.out.println(f.getPath());
        System.out.println(f.getAbsolutePath());
        System.out.println(f.getCanonicalPath());
        System.out.println("**********");
        /*
        相对路径:一定要找对应的路径作为文件描述的基准
        Tomcat:如果打了个 war 包,在 Tomcat 中运行,部署在 webapps 里,基准路径就会是对应 Tomcat 工作的 bin 目录
        打了个 jar 包,通过 java -jar [jar包名],基准路径就是运行 java 命令所在的路径
        如果通过 IDEA 右上角的绿色三角符号运行,基准路径就是整个项目下的路径「和src统计目录」
         */
        File f1 = new File("./test.txt");
        System.out.println(f1.getParent());
        System.out.println(f1.getName());
        System.out.println(f1.getPath());
        System.out.println(f1.getAbsolutePath());
        System.out.println(f1.getCanonicalPath());
    }
}
// 运行结果:
/Users/cxf/java/4.System/src/file/content
test.txt
/Users/cxf/java/4.System/src/file/content/test.txt
/Users/cxf/java/4.System/ /Users/cxf/java/4.System/src/file/content/test.txt
/Users/cxf/java/4.System/ /Users/cxf/java/4.System/src/file/content/test.txt
**********
.
test.txt
./test.txt
/Users/cxf/java/4.System/./test.txt
/Users/cxf/java/4.System/test.txt

1.2 创建文件

package file;

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

public class Demo2 {
    public static void main(String[] args) throws IOException {
        File f = new File("./test.txt");
        System.out.println(f.exists());
        System.out.println(f.isDirectory());
        System.out.println(f.isFile());
        System.out.println("**********");
        // 创建一个文件
        System.out.println(f.createNewFile());
        System.out.println(f.exists());
        System.out.println(f.isDirectory());
        System.out.println(f.isFile());
    }
}
// 运行结果:
true
false
true
**********
false
true
false
true

1.3 删除文件

「立即删除」

package file;

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

// 文件删除
public class Demo3 {
    public static void main(String[] args) throws IOException {
        File f = new File("./deleted.txt");
        System.out.println(f.exists());
        System.out.println("创建文件之前");
        System.out.println("**********");
        System.out.println(f.createNewFile());
        System.out.println("创建文件之后");
        System.out.println(f.exists());
        System.out.println("删除文件之前");
        f.delete();
        System.out.println("删除文件之后");
        System.out.println(f.exists());
    }
}

// 运行结果:
false
创建文件之前
**********
true
创建文件之后
true
删除文件之前
删除文件之后
false

「删除动作会在JVM运行结束的时候执行」

package file;

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

public class Demo4 {
    public static void main(String[] args) throws IOException {
        File f = new File("./delete.txt");
        f.createNewFile();
        f.deleteOnExit();
        // 在这里加一个阻塞,方便看到
        new Scanner(System.in).nextInt();
    }
}

1.4 创建目录

package file;

import java.io.File;

public class Demo5 {
    public static void main(String[] args) {
        File f=new File("./src/file/content/testDir");
        /*
        mkdir:创建单个目录
        mkdirs:还会创建中间的文件夹目录
         */
        f.mkdirs();
    }
}

返回值类型

函数

意义

String

getParent

返回File对象的父目录

String

getName

返回File对象的纯文件名

String

getPath

返回File对象的文件夹路径

String

getAbsolutePath

返回File对象的绝对路径

String

getCanonicalPath

返回File对象修饰过的绝对路径

boolean

exists

判断File对象是否存在

boolean

isFile

判断File对象是否是文件

boolean

isDirectory

判断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

判断用户对文件是否有写权限

1.5 文件改名

package file;

import java.io.File;

public class Demo6 {
    public static void main(String[] args) {
        File f = new File("./src/file/content/test.txt");
        File f1 = new File("./src/file/content/test.txt");
        f.renameTo(f1);
    }
}

2. 文件的读写

  • 字节流:以字节为单位,针对二进制文件「InputStream:负责读;OutputStream:负责写」
  • 字符流:以字符为单位,针对文本「Reader:负责读;Writer:负责写」

2.1 字节流读

package file;

import java.io.*;

// 字节流读取文件
public class Demo7 {
    public static void main(String[] args) {
        /*
        InputStream:是一个抽象类,是总领全局的,表示所有的按二进制读取文件的类
        构造方法中参数:
            打开的文件名:相对路径/绝对路径
            也可以是一个 File 对象
         */
//        InputStream inputStream = null;
//        try {
//            inputStream = new FileInputStream("./src/file/content/test.txt");
//            OutputStream outputStream = new FileOutputStream("./src/file/content/write.txt");
//            while (true) {
//                /*
//                用 int 而不是用 byte 是因为:读出来的数据范围[0, 255]
//                -1代表读完了
//                byte[-127, 128],一个字节
//                 */
//                int b = inputStream.read();
//                if (b == -1) {
//                    break;
//                }
//                System.out.println(b);
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        } finally {
//            // 把 close 放在 finally 中保证即使代码出现异常也能关闭,但是代码看的太不方便
//            try {
//                inputStream.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }

        // try white resources 把要释放的资源放到 try() 然后就会自动调用到关闭:要求 try() 里的对象能够实现 Closeable 接口。文件流对象都是实现了 Closeable
        try (InputStream inputStream = new FileInputStream("./src/file/content/test.txt")) {
            while (true) {
                int b = inputStream.read();
                if (b == -1) {
                    break;
                }
                System.out.println(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码是对上述注释代码的优化

Java读取远程硬件信息 使用java读取硬件设备入门_System

  • read:一次读一个字节
  • read(byte[] b):一次读若干个字节,尽可能的填满这个字节数组
  • 如果文件中剩余的数据比较多,超过了数组的长度,就会直接返回数组长度(把数组填满)
  • 如果文件中剩余的数据比较少,不超过参数数组的长度,此时就会直接返回时记得元素个数
  • read(byte[] b, int off, int len):加了一个起始和终止

为何选用int而不是byte接受inputStream

Java读取远程硬件信息 使用java读取硬件设备入门_Java文件_02

byte[-127, 128]

而读取数据遇到中文等特殊字符用的是 Unicode编码,而数据范围已经由之前的 ASCII码进行扩展,最大支持 128,已经无法满足,所以需要 255 来支撑

按照指定字节数读取

package file;

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

public class Demo8 {
    public static void main(String[] args) {
        // 尝试一次读取 1024 个字节
        try (InputStream inputStream = new FileInputStream("./src/file/content/test.txt")) {
            byte[] buffer = new byte[1024];
            while (true) {
                int len = inputStream.read(buffer);
                if (len == -1) {
                    break;
                }
                // 遍历每个字节
//                for (int b:buffer) {
//                    System.out.print(b);
//                }
                String s = new String(buffer, 0, len, "UTF-8");
                System.out.println(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.2 字符流读

Java读取远程硬件信息 使用java读取硬件设备入门_删除文件_03

package file;

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

public class Demo9 {
    public static void main(String[] args) {
        try(Reader reader=new FileReader("./src/file/content/test.txt")){
            while (true){
                int c=reader.read();
                if (c==-1){
                    break;
                }
                System.out.print((char)c);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

读字符,一次读的是一个 char;读字节,一次读的是一个 byte

2.3 字节流写

Java读取远程硬件信息 使用java读取硬件设备入门_删除文件_04

package file;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Demo10 {
    public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("./src/file/content/test.txt")) {
            // 使用 write 会清空原始数据
            outputStream.write(65);
            outputStream.write(66);
            outputStream.write(67);
            outputStream.write(68);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.4 字符流写

OutputStream.write()可传入的参数

Java读取远程硬件信息 使用java读取硬件设备入门_删除文件_05

package file;

import java.io.*;

public class Demo11 {
    public static void main(String[] args) {
        try (Writer writer = new FileWriter("./src/file/content/test.txt")) {
            writer.write('朱');
            writer.write("🐷");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. 案例

3.1 删除指定文件

package file;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

// 根据指定的路径,堆路径进行遍历,删除符合要求的文件
public class Demo12 {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        // 1.提示用户输入一个带扫描的路径
        System.out.print("请输入要扫描的目录路径:");
        String rootDirPath = scanner.next();
        // 2.约定一下这个路径是否合法
        File rootDir = new File(rootDirPath);
        if (!rootDir.isDirectory()) {
            // 如果给定的这个路径不存在,或者不是目录,就直接出错
            System.out.println("您输入的扫描路径非法");
            return;
        }
        // 3.输入要删除的文件
        System.out.print("请输入要删除的文件名:");
        String deleteFileName = scanner.next();
        // 4.遍历当前目录,找到所有文件名和带删除文件名匹配的文件
        List<File> result = new ArrayList<>();
        // 通过 scanDir 这个方法把所有和 deleteFileName 匹配的文件都给找出来,放到 result 中「这个遍历文件的过程就需要用到递归」
        scanDir(rootDir, deleteFileName, result);
        // 5.进行删除,把 result 里找到的所有文件,都依次进行删除
        for (File f : result) {
            System.out.print(f.getCanonicalPath() + "该文件是否确认删除?y/n");
            String choice = scanner.next();
            if ("y".equals(choice) || "Y".equals(choice)) {
                f.delete();
                System.out.println(f.getCanonicalPath() + "删除成功");
            }
        }
    }

    private static void scanDir(File rootDir, String deleteFileName, List<File> result) throws IOException {
        // 1. 首先,先罗列出 rootDir 下都有哪些文件
        File[] files = rootDir.listFiles();
        for (File f : files) {
            System.out.println("扫描到了文件:" + f.getCanonicalPath());
            if (f.isFile()) {
                // 是普通文件
                if (f.getName().equals(deleteFileName)) {
                    result.add(f);
                }
            } else if (f.isDirectory()) {
                // 是目录,递归调用 scnDir 方法,针对这个目录,在进行进一步的判定
                scanDir(f, deleteFileName, result);
            }
        }
    }
}

3.2 文件拷贝

package file;

import java.io.*;
import java.util.Scanner;

// 文件拷贝
public class Demo13 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入复制的源文件:");
        String src = scanner.next();
        System.out.print("请输入要复制的目标文件");
        String dst = scanner.next();

        // 判定一下 src 是否存在
        File srcFile = new File(src);
        if (!srcFile.isFile()) {
            System.out.println("当前输入的路径有误,不是一个合法的路径!");
            return;
        }

        // 进行复制操作「打开src,把内容一个字节一个字节写入」
        try (InputStream inputStream = new FileInputStream(src); OutputStream outputStream = new FileOutputStream(dst)) {
            // 每次尝试读一个 byte[],把这整个数组都给写过去
            byte[] buffer = new byte[1024];
            while (true) {
                int len = inputStream.read(buffer);
                if (len == -1) {
                    break;
                }
                // 读取成功,写入到 outputStream 中
                outputStream.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.3 查找目录下是否包含某个字符串

package file;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

// 在指定目录中查找包含制定字符串的文件「比较低效的代码」
public class Demo14 {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        // 1. 先输入要获取到的目录和内容
        System.out.print("请输入要遍历的目录:");
        String rootDirPath = scanner.next();
        System.out.print("请输入要查找的字符串:");
        String content = scanner.next();
        File rootDir = new File(rootDirPath);
        if (!rootDir.isDirectory()) {
            System.out.println("输入的路径存在问题!");
            return;
        }
        // 2.进行递归遍历,找到所有符合要求的文件
        List<File> results = new ArrayList<>();
        scanDirWithContent(rootDir, content, results);
        // 3.打印一下找到的文件结果
        for (File f : results) {
            System.out.println(f.getCanonicalPath());
        }
    }

    private static void scanDirWithContent(File rootDir, String content, List<File> results) {
        // 1.先列出 rootDir 中所有的文件
        File[] files = rootDir.listFiles();
        if (files == null){
            // 空目录
            return;
        }
        // 2.依次遍历每个文件,进行判定,如果是目录就进行递归
        for (File f : files) {
            if (f.isFile()) {
                // 如果是普通文件,就判定一下 content 是否被这个文件包含
                if (isContainsExist(f, content)) {
                    // 如果条件为 true,说明文件 f 包含 content,添加到 results
                    results.add(f);
                }
            } else if (f.isDirectory()) {
                // 是目录就递归
                scanDirWithContent(f, content, results);
            }
        }
    }

    private static boolean isContainsExist(File f, String content) {
        // 1.读取 f 的内容,把内容放到一个 String 类
        StringBuilder stringBuilder = new StringBuilder();
        try (Reader reader = new FileReader(f)) {
            while (true) {
                int c = reader.read();
                if (c == -1) {
                    break;
                }
                stringBuilder.append((char) c);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 2.判定 content 是否是读取的 String 的子串
        return stringBuilder.indexOf(content) != -1;
    }
}