Java的文件操作——IO

1、认识文件

狭义上的文件(file)。针对硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时,
往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概 念,就类似办公桌上的一份份真实的文件一般。
文件除了有数据内容之外,还有一部分信息,例如文件名、文件类型、文件大小等并不作为文件的数据而存在,我们把这部分信息可以视为文件的元信息。
在这里插入图片描述

2、树型结构组织 和 目录

随着文件越来越多,对文件的系统管理也被提上了日程,如何进行文件的组织呢,一种合乎自然的想法出现了,就是按照层级结构进行组织 —— 也就是我们数据结构中学习过的树形结构。这样,一种专门用来存放管理信息的特殊文件诞生了,也就是我们平时所谓文件夹(folder)或者目录(directory)的概念。
在这里插入图片描述

在这里插入图片描述

3、文件路径(Path)

如何在文件系统中如何定位我们的一个唯一的文件就成为当前要解决的问题,但这难不倒计算机科学家,因为从树型结构的角度来看,树中的每个结点都可以被一条从根开始,一直到达的结点的路径所描述,而这种描述方式就被称为文件的绝对路径(absolute path)。
在这里插入图片描述
除了可以从根开始进行路径的描述,我们可以从任意结点出发,进行路径的描述,而这种描述方式就被称为相对路径(relative path),相对于当前所在结点的一条路径。
在这里插入图片描述
其他知识
即使是普通文件,根据其保存数据的不同,也经常被分为不同的类型,我们一般简单的划分为文本文件和二进制文件,分别指代保存被字符集编码的文本和按照标准格式保存的非被字符集编码过的文件。
Windows 操作系统上,会按照文件名中的后缀来确定文件类型以及该类型文件的默认打开程序。但这个习俗并不是通用的,在 OSX、Unix、Linux 等操作系统上,就没有这样的习惯,一般不对文件类型做如此精确地分类。
在这里插入图片描述
文件由于被操作系统进行了管理,所以根据不同的用户,会赋予用户不同的对待该文件的权限,一般地可以认为有可读、可写、可执行权限。
在这里插入图片描述
Windows 操作系统上,还有一类文件比较特殊,就是平时我们看到的快捷方式(shortcut),这种文件只是对真实文件的一种引用而已。其他操作系统上也有类似的概念,例如,软链接(soft link)等

4、Java 中操作文件

Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意,有 File 对象,并不代表真实存在该文件。

4.1 File 概述

我们先来看看 File 类中的常见属性、构造方法和方法

4.1.1 属性
修饰符及类型属性说明
static StringpathSeparator依赖于系统的路径分隔符,String 类型的表示
static charpathSeparator依赖于系统的路径分隔符,char 类型的表示
4.1.2构造方法
签名说明
File(File parent, Stringchild)根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(String pathname)根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File(String parent, Stringchild)根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示
4.1.3 方法
修饰符及返回值类型方法签名说明
StringgetParent()返回 File 对象的父目录文件路径
StringgetName()返回 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(Filedest)进行文件改名,也可以视为我们平时的剪切、粘贴操作
booleancanRead()判断用户是否对文件有可读权限
booleancanWrite()判断用户是否对文件有可写权限

代码示例
观察 get 系列的特点和差异

import java.io.File;
import java.io.IOException;
public class test3 {
    // File 的构造
        public static void main(String[] args) throws IOException {
            File f = new File("d:\\test.txt");
            System.out.println(f.getParent()); // 获取到文件的父目录
            System.out.println(f.getName());    // 获取到文件名
            System.out.println(f.getPath());    // 获取到文件路径(构造 File 的时候指定的路径)
            System.out.println(f.getAbsolutePath()); // 获取到绝对路径
            System.out.println(f.getCanonicalPath()); // 获取到绝对路径

            System.out.println("=================");

            File f2 = new File("./test.txt");
            System.out.println(f2.getParent()); // 获取到文件的父目录
            System.out.println(f2.getName());    // 获取到文件名
            System.out.println(f2.getPath());    // 获取到文件路径(构造 File 的时候指定的路径)
            System.out.println(f2.getAbsolutePath()); // 获取到绝对路径
            System.out.println(f2.getCanonicalPath()); // 获取到绝对路径
        }

}

结果:
在这里插入图片描述
普通文件的创建、删除

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 方弟全
 * Date: 2022-04-05
 * Time: 10:46
 */
public class test3 {
    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());
        System.out.println(file.createNewFile());
        System.out.println(file.exists());
        System.out.println(file.isDirectory());
        System.out.println(file.isFile());
        System.out.println(file.createNewFile());
    }
}

结果:
在这里插入图片描述
普通文件的删除

import java.io.File;
import java.io.IOException;
public class test3 {
    public static void main(String[] args) throws IOException {
        File file = new File("some-file.txt"); // 要求该文件不存在,才能看到相同的现象
        System.out.println(file.exists());
        System.out.println(file.createNewFile());
        System.out.println(file.exists());
        System.out.println(file.delete());
        System.out.println(file.exists());

    }
}

在这里插入图片描述

观察 deleteOnExit 的现象

import java.io.File;
import java.io.IOException;
public class test3 {
    public static void main(String[] args) throws IOException {
        File file = new File("some-file.txt"); // 要求该文件不存在,才能看到相同的现象
        System.out.println(file.exists());
        System.out.println(file.createNewFile());
        System.out.println(file.exists());
        file.deleteOnExit();
        System.out.println(file.exists());

    }
}

在这里插入图片描述
观察目录的创建

import java.io.File;
import java.io.IOException;
public class test3 {
    public static void main(String[] args) throws IOException {
        File dir = new File("some-dir"); // 要求该目录不存在,才能看到相同的现象
        System.out.println(dir.isDirectory());
        System.out.println(dir.isFile());
        System.out.println(dir.mkdir());
        System.out.println(dir.isDirectory());
        System.out.println(dir.isFile());

    }
}

在这里插入图片描述
观察目录创建2

import java.io.File;
import java.io.IOException;
public class Main {
    public static void main(String[] args) throws IOException {
        File dir = new File("some-parent\\some-dir"); // some-parent 和 somedir 都不存在
        System.out.println(dir.isDirectory());
        System.out.println(dir.isFile());
        System.out.println(dir.mkdir());
        System.out.println(dir.isDirectory());
        System.out.println(dir.isFile());
   }
}

运行结果
false
false
false
false
false
mkdir() 的时候,如果中间目录不存在,则无法创建成功; mkdirs() 可以解决这个问题。

import java.io.File;
import java.io.IOException;
public class Main {
    public static void main(String[] args) throws IOException {
        File dir = new File("some-parent\\some-dir"); // some-parent 和 somedir 都不存在
        System.out.println(dir.isDirectory());
        System.out.println(dir.isFile());
        System.out.println(dir.mkdirs());
        System.out.println(dir.isDirectory());
        System.out.println(dir.isFile());
   }
}

运行结果
false
false
true
true
false
观察文件重命名

import java.io.File;
import java.io.IOException;
public class Main {
    public static void main(String[] args) throws IOException {
        File file = new File("some-file.txt"); // 要求 some-file.txt 得存在,可以
是普通文件,可以是目录
        File dest = new File("dest.txt");   // 要求 dest.txt 不存在
        System.out.println(file.exists());
        System.out.println(dest.exists());
        System.out.println(file.renameTo(dest));
        System.out.println(file.exists());
        System.out.println(dest.exists());
   }
}

运行结果
true
false
true
false
true

5、文件内容的读写 —— 数据流

在这里插入图片描述

5.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。

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

public class Demo8 {
    public static void main(String[] args) {
        // 构造方法中需要指定打开文件的路径.
        // 此处的路径可以是绝对路径, 也可以是相对路径, 还可以是 File 对象
       InputStream inputStream = null;
       try {
           // 1. 创建对象, 同时也是在打开文件.
           inputStream = new FileInputStream("d:/test.txt");
           // 2. 尝试一个一个字节的读, 把整个文件都读完.
           while (true) {
               int b = inputStream.read();
              if (b == -1) {
                   // 读到了文件末尾
                   break;
                }
                System.out.println(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 3. 读完之后要记得关闭文件, 释放资源~
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
            while (true) {
                int b = inputStream.read();
               if (b == -1) {
                    break;
               }
                System.out.println(b);
           }
       } catch (IOException e) {
            e.printStackTrace();
        }

5.2、FileInputStream 概述

构造方法

签名说明
FileInputStream(File file)利用 File 构造文件输入流
FileInputStream(String name)利用文件路径构造文件输入流
```java
import java.io.*;
// 需要先在项目目录下准备好一个 hello.txt 的文件,里面填充 "Hello" 的内容
public class Main {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
            byte[] buf = new byte[1024];
            int len;
            
            while (true) {
                len = is.read(buf);
                if (len == -1) {
                    // 代表文件已经全部读完
                    break;
               }
                
                for (int i = 0; i < len; i++) {
               System.out.printf("%c", buf[i]);
               }
           }
       }
   }
}

代码示例
将文件完全读完的两种方式。相比较而言,后一种的 IO 次数更少,性能更好。

import java.io.*;
// 需要先在项目目录下准备好一个 hello.txt 的文件,里面填充 "Hello" 的内容
public class Main {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
            while (true) {
                int b = is.read();
                if (b == -1) {
                    // 代表文件已经全部读完
                    break;
               }
                
                System.out.printf("%c", b);
           }
       }
   }
}

这里我们把文件内容中填充中文看看,注意,写中文的时候使用 UTF-8 编码。hello.txt 中填写 “你好中国”
注意:这里我利用了这几个中文的 UTF-8 编码后长度刚好是 3 个字节和长度不超过 1024 字节的现状,但这种方式并不是通用的

import java.io.*;
// 需要先在项目目录下准备好一个 hello.txt 的文件,里面填充 "你好中国" 的内容
public class Main {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
            byte[] buf = new byte[1024];
            int len;
            while (true) {
                len = is.read(buf);
                if (len == -1) {
                    // 代表文件已经全部读完
                    break;
               }
                // 每次使用 3 字节进行 utf-8 解码,得到中文字符
                // 利用 String 中的构造方法完成
                // 这个方法了解下即可,不是通用的解决办法
                for (int i = 0; i < len; i += 3) {
                    String s = new String(buf, i, 3, "UTF-8");
                    System.out.printf("%s", s);
               }
           }
       }
   }
}

6、利用 Scanner 进行字符读取

,我们看到了对字符类型直接使用 InputStream 进行读取是非常麻烦且困难的,所以,我们使用一种我们之前比较熟悉的类来完成该工作,就是 Scanner 类。

构造方法说明
Scanner(InputStream is, String charset)使用 charset 字符集进行 is 的扫描读取
import java.io.*;
import java.util.*;
// 需要先在项目目录下准备好一个 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);
               }
           }
       }
   }
}

7、OutputStream 概述

方法

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

8、利用 OutputStreamWriter 进行字符写入

import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            os.write('H');
            os.write('e');
            os.write('l');
            os.write('l');
            os.write('o');
            // 不要忘记 flush
            os.flush();
       }
   }
}
import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            byte[] b = new byte[] {
               (byte)'G', (byte)'o', (byte)'o', (byte)'d'
           };
            os.write(b);
          
            // 不要忘记 flush
            os.flush();
       }
   }
}
import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            byte[] b = new byte[] {
               (byte)'G', (byte)'o', (byte)'o', (byte)'d', (byte)'B', 
(byte)'a', (byte)'d'
           };
            os.write(b, 0, 4);
          
            // 不要忘记 flush
            os.flush();
       }
   }
}
import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
           String s = "Nothing";
            byte[] b = s.getBytes();
            os.write(b);
          
            // 不要忘记 flush
            os.flush();
       }
   }
}
import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            String s = "你好中国";
            byte[] b = s.getBytes("utf-8");
         os.write(b);
         // 不要忘记 flush
            os.flush();
       }
   }
}

9、利用 PrintWriter 找到我们熟悉的方法

上述,我们其实已经完成输出工作,但总是有所不方便,我们接来下将 OutputStream 处理下,使用PrintWriter 类来完成输出,因为
PrintWriter 类中提供了我们熟悉的print/println/printf 方法

OutputStream os = ...;
OutputStreamWriter osWriter = new OutputStreamWriter(os, "utf-8"); // 告诉它,我
们的字符集编码是 utf-8PrintWriter writer = new PrintWriter(osWriter);
// 接下来我们就可以方便的使用 writer 提供的各种方法了
writer.print("Hello");
writer.println("你好");
writer.printf("%d: %s\n", 1, "没什么");
// 不要忘记 flush
writer.flush();

示例1

import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            try (OutputStreamWriter osWriter = new OutputStreamWriter(os, "UTF8")) {
                try (PrintWriter writer = new PrintWriter(osWriter)) {
                    writer.println("我是第一行");
                    writer.print("我的第二行\r\n");
                    writer.printf("%d: 我的第三行\r\n", 1 + 1);
                    writer.flush();
               }
           }
       }
   }
}

10、小程序练习

我们学会了文件的基本操作 + 文件内容读写操作,接下来,我们实现一些小工具程序,来锻炼我们的能力。

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

import java.io.*;
import java.util.*;
public class Main {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入要扫描的根目录(绝对路径 OR 相对路径): ");
        String rootDirPath = scanner.next();
        File rootDir = new File(rootDirPath);
        if (!rootDir.isDirectory()) {
            System.out.println("您输入的根目录不存在或者不是目录,退出");
            return;
       }
        System.out.print("请输入要找出的文件名中的字符: ");
        String token = scanner.next();
        List<File> result = new ArrayList<>();
        // 因为文件系统是树形结构,所以我们使用深度优先遍历(递归)完成遍历
        scanDir(rootDir, token, result);
        System.out.println("共找到了符合条件的文件 " + result.size() + " 个,它们分别
是");
        for (File file : result) {
            System.out.println(file.getCanonicalPath() + "   请问您是否要删除该文
件?y/n");
            String in = scanner.next();
            if (in.toLowerCase().equals("y")) {
                file.delete();
           }
       }
   }
    private static void scanDir(File rootDir, String token, List<File> result) {
        File[] files = rootDir.listFiles();
        if (files == null || files.length == 0) {
            return;
       }
        for (File file : files) {
            if (file.isDirectory()) {
                scanDir(file, token, result);
           } else {
                if (file.getName().contains(token)) {
                    result.add(file.getAbsoluteFile());
               }
           }
       }
   }
}

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

// 案例1, 实现查找文件并删除
public class Demo12 {
    public static void main(String[] args) {
        // 1. 先输入要扫描的目录, 以及要删除的文件名
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要扫描的路径: ");
        String rootDirPath = scanner.next();
        System.out.println("请输入要删除的文件名: ");
        String toDeleteName = scanner.next();
        File rootDir = new File(rootDirPath);
        if (!rootDir.isDirectory()) {
            System.out.println("输入的扫描路径有误!");
            return;
        }
        // 2. 遍历目录, 把 指定目录 中的所有文件和子目录都遍历一遍, 从而找到要删除的文件
        //    通过这个方法来实现递归遍历并删除的操作
        scanDir(rootDir, toDeleteName);
    }

    private static void scanDir(File rootDir, String toDeleteName) {
        // 1. 先列出 rootDir 中都有哪些内容
        File[] files = rootDir.listFiles();
        if (files == null) {
            // rootDir 是一个空目录
            return;
        }
        // 2. 遍历当前列出的这些内容. 如果是普通文件, 就检测文件名是否是要删除的文件.
        //    如果是目录, 就递归的进行遍历
        for (File f : files) {
            if (f.isFile()) {
                // 普通文件的情况
                if (f.getName().contains(toDeleteName)) {
                    // 不要求名字完全一样, 只要文件名中包含了关键字即可删除
                    // 就进行删除操作
                    deleteFile(f);
                }
            } else if (f.isDirectory()) {
                // 目录就递归的进行遍历
                scanDir(f, toDeleteName);
            }
        }
    }

    private static void deleteFile(File f) {
        try {
            System.out.println(f.getCanonicalPath() + " 确认要删除吗? (Y/n)");
            Scanner scanner = new Scanner(System.in);
            String choice = scanner.next();
            if (choice.equals("Y") || choice.equals("y")) {
                f.delete();
                System.out.println("文件删除成功!");
            } else {
                System.out.println("文件取消删除!");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例2
进行普通文件的复制

import java.io.*;
import java.util.*;
public class Main {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        
        System.out.print("请输入要复制的文件(绝对路径 OR 相对路径): ");
        String sourcePath = scanner.next();
        File sourceFile = new File(sourcePath);
        if (!sourceFile.exists()) {
            System.out.println("文件不存在,请确认路径是否正确");
            return;
       }
        
        if (!sourceFile.isFile()) {
            System.out.println("文件不是普通文件,请确认路径是否正确");
            return;
       }
        System.out.print("请输入要复制到的目标路径(绝对路径 OR 相对路径): ");
        String destPath = scanner.next();
        File destFile = new File(destPath);
        if (destFile.exists()) {
            if (destFile.isDirectory()) {
                System.out.println("目标路径已经存在,并且是一个目录,请确认路径是否正
确");
                return;
           }
            
            if (destFile.isFile()) {
                System.out.println("目录路径已经存在,是否要进行覆盖?y/n");
                String ans = scanner.next();
                if (!ans.toLowerCase().equals("y")) {
                    System.out.println("停止复制");
                    return;
               }
           }
       }
        
        try (InputStream is = new FileInputStream(sourceFile)) {
            try (OutputStream os = new FileOutputStream(destFile)) {
                byte[] buf = new byte[1024];
                int len;
                
                while (true) {
                    len = is.read(buf);
                    if (len == -1) {
                        break;
                   }
                    
                    os.write(buf, 0, len);
               }
                
                os.flush();
           }
       }
System.out.println("复制已完成");
   }
}
import java.io.*;
import java.util.Scanner;

public class Demo13 {
    public static void main(String[] args) {
        // 1. 输入两个路径
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要拷贝的源路径: ");
        String src = scanner.next();
        System.out.println("请输入要拷贝的目标路径: ");
        String dest = scanner.next();
        File srcFile = new File(src);
        if (!srcFile.isFile()) {
            System.out.println("输入的源路径不正确!");
            return;
        }
        // 此处不太需要检查目标文件是否存在. OutputStream 写文件的时候能够自动创建不存在的文件.
        // 2. 读取源文件, 拷贝到目标文件中
        try (InputStream inputStream = new FileInputStream(src)) {
            try (OutputStream outputStream = new FileOutputStream(dest)) {
                // 把 inputStream 中的数据读出来, 写入到 outputStream 中
                byte[] buffer = new byte[1024];
                while (true) {
                    int len = inputStream.read(buffer);
                    if (len == -1) {
                        // 读取完毕
                        break;
                    }
                    // 写入的时候, 不能把整个 buffer 都写进去. 毕竟 buffer 可能是只有一部分才是有效数据.
                    outputStream.write(buffer, 0, len);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例3
扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)
注意:我们现在的方案性能较差,所以尽量不要在太复杂的目录下或者大文件下实验

import java.io.*;
import java.util.*;
public class Main {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入要扫描的根目录(绝对路径 OR 相对路径): ");
        String rootDirPath = scanner.next();
        File rootDir = new File(rootDirPath);
        if (!rootDir.isDirectory()) {
            System.out.println("您输入的根目录不存在或者不是目录,退出");
            return;
       }
        System.out.print("请输入要找出的文件名中的字符: ");
        String token = scanner.next();
        List<File> result = new ArrayList<>();
        // 因为文件系统是树形结构,所以我们使用深度优先遍历(递归)完成遍历
        scanDirWithContent(rootDir, token, result);
        System.out.println("共找到了符合条件的文件 " + result.size() + " 个,它们分别
是");
        for (File file : result) {
            System.out.println(file.getCanonicalPath());
       }
   }
    private static void scanDirWithContent(File rootDir, String token, 
List<File> result) throws IOException {
        File[] files = rootDir.listFiles();
        if (files == null || files.length == 0) {
            return;
       }
        for (File file : files) {
            if (file.isDirectory()) {
                scanDirWithContent(file, token, result);
           } else {
                if (isContentContains(file, token)) {
                    result.add(file.getAbsoluteFile());
               }
           }
       }
   }
  // 我们全部按照utf-8的字符文件来处理
    private static boolean isContentContains(File file, String token) throws
IOException {
        StringBuilder sb = new StringBuilder();
        try (InputStream is = new FileInputStream(file)) {
            try (Scanner scanner = new Scanner(is, "UTF-8")) {
                while (scanner.hasNextLine()) {
                    sb.append(scanner.nextLine());
                    sb.append("\r\n");
               }
           }
       }
        
        return sb.indexOf(token) != -1;
   }
}

如何按字节进行数据读

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

public class Demo8 {
    public static void main(String[] args) {
        // 构造方法中需要指定打开文件的路径.
        // 此处的路径可以是绝对路径, 也可以是相对路径, 还可以是 File 对象
//        InputStream inputStream = null;
//        try {
//            // 1. 创建对象, 同时也是在打开文件.
//            inputStream = new FileInputStream("d:/test.txt");
//            // 2. 尝试一个一个字节的读, 把整个文件都读完.
//            while (true) {
//                int b = inputStream.read();
//                if (b == -1) {
//                    // 读到了文件末尾
//                    break;
//                }
//                System.out.println(b);
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        } finally {
//            // 3. 读完之后要记得关闭文件, 释放资源~
//            try {
//                inputStream.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }

//        try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
//            while (true) {
//                int b = inputStream.read();
//                if (b == -1) {
//                    break;
//                }
//                System.out.println(b);
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

        try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
            // 一次读取若干个字节.
            while (true) {
                byte[] buffer = new byte[1024];
                int len = inputStream.read(buffer);
                if (len == -1) {
                    // 如果返回 -1 说明读取完毕了
                    break;
                }
                for (int i = 0; i < len; i++) {
                    System.out.println(buffer[i]);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

如何按字节进行数据写

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

// 使用字节流, 写文件的案例.
public class Demo9 {
    public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("d:/test.txt")) {
//            outputStream.write(97);
//            outputStream.write(98);
//            outputStream.write(99);

            byte[] buffer = new byte[]{97, 98, 99};
            outputStream.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

如何按字符进行数据读

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

// 按照字符来读写
public class Demo10 {
    public static void main(String[] args) {
        try (Reader reader = new FileReader("d:/test.txt")) {
            // 按照字符来读.
            while (true) {
                char[] buffer = new char[1024];
                int len = reader.read(buffer);
                if (len == -1) {
                    break;
                }
//                for (int i = 0; i < len; i++) {
//                    System.out.println(buffer[i]);
//                }
                // 如果这里传入的 数组 是 byte 数组, 还可以手动的指定一下 utf8 字符集避免乱码.
                String s = new String(buffer, 0, len);
                System.out.println(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

如何按字符进行数据写

```java
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

// 按照字符来写
public class Demo11 {
    public static void main(String[] args) {
        try (Writer writer = new FileWriter("d:/test.txt")) {
            writer.write("xyz");
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值