4.文件操作和IO

文章目录

  • 1.认识文件
    • 1.1树型结构组织 和 目录
    • 1.2文件路径(Path)
    • 1.3其他知识
  • 2.Java 中操作文件
    • 2.1File 概述
      • 2.1.1属性
      • 2.1.2构造方法
      • 2.1.3方法
    • 2.2代码示例
      • 2.2.1示例1-get 系列的特点和差异
      • 2.2.2示例2-普通文件的创建、删除
      • 2.2.3示例3-普通文件的删除
      • 2.2.4示例4-deleteOnExit 的现象
      • 2.2.5示例5-目录的创建
      • 2.2.6示例6-目录创建2
      • 2.2.7示例7-文件重命名
  • 3.文件内容的读写 —— 数据流
    • 3.1InputStream 概述 - 读文件
      • 3.1.1方法
      • 3.1.2说明
    • 3.2FileInputStream 概述 - 读文件(字节流)
      • 3.2.1构造方法
    • 3.3代码示例
      • 3.3.1示例1
      • 3.3.2示例2
    • 3.4利用 Scanner 进行字符读取
      • 3.4.1示例1
    • 3.5OutputStream 概述 - 写文件(字节流)
      • 3.5.1方法
      • 3.5.2说明
    • 3.6利用 OutputStreamWriter 进行字符写入
      • 3.6.1示例1
    • 3.7利用 PrintWriter 找到我们熟悉的方法
      • 3.7.1示例1
    • 3.8为什么InputStream是读文件而OutputStream是写文件?
    • 3.9Reader 和 Writer (字符流)
      • 3.9.1Reader
      • 3.9.2Writer
    • 3.10 字节 和 字符的区别
  • 4.小程序练习
    • 4.1示例1
    • 4.2示例2
    • 4.3示例3
  • 5.代码参考
    • 5.1如何按字节进行数据读
    • 5.2如何按字节进行数据写
    • 5.3如何按字符进行数据读
    • 5.4如何按字符进行数据写

大家好,我是晓星航。今天为大家带来的是 文件操作和IO 相关的讲解!😀

1.认识文件

我们先来认识狭义上的文件(file)。针对硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念, 就类似办公桌上的一份份真实的文件一般。

文件除了有数据内容之外,还有一部分信息,例如文件名、文件类型、文件大小等并不作为文件的数据 而存在,我们把这部分信息可以视为文件的元信息。

1.1树型结构组织 和 目录

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

目录 -> 文件夹

/ 斜杠 / 可以用来分割不同的目录级别 在windows下使用\反斜杠也可以用来分隔不同的目录级别

\ 反斜杠

上述路径可以表示成:d:/steam/steamapps/common

windows下默认是使用\反斜杠来写的

即d:\steam\steamapps\common这样写也可以

1.2文件路径(Path)

如何在文件系统中如何定位我们的一个唯一的文件就成为当前要解决的问题,但这难不倒计算机科学家,因为从树型结构的角度来看,树中的每个结点都可以被一条从根开始,一直到达的结点的路径所描述,而这种描述方式就被称为文件的绝对路径(absolute path)。

绝对路径 以 c: d: 盘符开头的路径(windows上盘符大小写都可以)

需要从磁盘头开始

除了可以从根开始进行路径的描述,我们可以从任意结点出发,进行路径的描述,而这种描述方式就被 称为相对路径(relative path),相对于当前所在结点的一条路径。

相对路径 以当前所在的目录为基准(工作目录),以 . 或者 … 开头(. 有时候可以省略),找到指定路径

  1. 例如我们要定位到111这个目录的相对路径

…/ 这个操作是表示返回到当前目录的上级目录

  1. windows下操作系统的默认路径

我们win+r 输入cmd后:

上述是我们默认的工作路径(目录)

如果我们敲一个d:

那么此时我们就来到了d盘的目录下面

如果我们敲一个cd epic

那么此时我们就来到了d盘里面的epic目录下

  1. idea的默认路径

idea的工作默认路径就是你的当前的项目所在目录。

如果代码中写了一些 相对路径的代码,工作路径就是以上述路径为基准的。

1.3其他知识

即使是普通文件,根据其保存数据的不同,也经常被分为不同的类型,我们一般简单的划分为文本文件 和二进制文件,分别指代保存被字符集编码的文本和按照标准格式保存的非被字符集编码过的文件。

Windows 操作系统上,会按照文件名中的后缀来确定文件类型以及该类型文件的默认打开程序。但这个 习俗并不是通用的,在 OSX、Unix、Linux 等操作系统上,就没有这样的习惯,一般不对文件类型做如 此精确地分类。

文件由于被操作系统进行了管理,所以根据不同的用户,会赋予用户不同的对待该文件的权限,一般地 可以认为有可读、可写、可执行权限。

Windows 操作系统上,还有一类文件比较特殊,就是平时我们看到的快捷方式(shortcut),这种文件 只是对真实文件的一种引用而已。其他操作系统上也有类似的概念,例如,软链接( soft link)等。

最后,很多操作系统为了实现接口的统一性,将所有的 I/O 设备都抽象成了文件的概念,使用这一理念 最为知名的就是 Unix、Linux 操作系统 —— 万物皆文件。

2.Java 中操作文件

本节内容中,我们主要涉及文件的元信息、路径的操作,暂时不涉及关于文件中内容的读写操作。

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

2.1File 概述

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

2.1.1属性

2.1.2构造方法

parent 表示当前文件所在的目录

child 表示自身的文件名

pathname 表示完整路径名

eg:

pathname d:/cat.jpg

parent d:/

child cat.jpg

2.1.3方法

输入输出的时候特别容易出现的异常

方法使用测试案例1:

这里我们查找d盘下的EDU/bin文件,明显他是存在的,且是目录。

然后我们修改一下将d:/EDU/bin换成./EDU/bin改为相对路径,这时我们就查找不到了,因为在我们test_20230807里面,我们是没有这个目录的。

那么此时我们修改一下创建一下这个文件再去查找,那么此时返回值就是我们预期的了。

deleteOnExit()方法删除临时文件

2.2代码示例

2.2.1示例1-get 系列的特点和差异

观察 get 系列的特点和差异

import java.io.File;
import java.io.IOException;
public class Main {public static void main(String[] args) throws IOException {File file = new File("..\\hello-world.txt"); // 并不要求该文件真实存在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());}
}

运行结果

..
hello-world.txt
..\hello-world.txt
D:\代码练习\文件示例1\..\hello-world.txt
D:\代码练习\hello-world.txt

2.2.2示例2-普通文件的创建、删除

普通文件的创建、删除

import java.io.File;
import java.io.IOException;
public class Main {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());}
}

运行结果

false
false
false
true
true
false
true
false

2.2.3示例3-普通文件的删除

普通文件的删除

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"); // 要求该文件不存在,才能看到相同的现象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());}
}

运行结果

false
true
true
true
false

2.2.4示例4-deleteOnExit 的现象

观察 deleteOnExit 的现象

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"); // 要求该文件不存在,才能看到相同的现象System.out.println(file.exists());System.out.println(file.createNewFile());System.out.println(file.exists());file.deleteOnExit();System.out.println(file.exists());}
}

运行结果

false
true
true
true

2.2.5示例5-目录的创建

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-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());}
}

运行结果

false
false
true
true
false

2.2.6示例6-目录创建2

观察目录创建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

2.2.7示例7-文件重命名

观察文件重命名

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

这里的 file.renameTo(dest) 是将file的文件名重命名为dest的文件名。

3.文件内容的读写 —— 数据流

3.1InputStream 概述 - 读文件

核心操作就是四个操作.

  1. 打开文件。(构造对象)
  2. 关闭文件。(close)
  3. 读文件(read) => 针对 InputStream / Reader
  4. 写文件(write) => 针对 OutputStream / Writer

抽象类和接口:

接口(Interface)是比抽象方法(abstract)更抽象的方法

接口提供的信息量更少

3.1.1方法

3.1.2说明

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

3.2FileInputStream 概述 - 读文件(字节流)

3.2.1构造方法

3.3代码示例

3.3.1示例1

版本1:

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

import java.io.*;public class IODemo3 {//使用一下字节流来读取文件public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("d:/test.txt");//进行读操作while (true) {int b = inputStream.read();if (b == -1) {//读取完毕break;}System.out.println("" + (byte)b);}inputStream.close();}
}

版本2:

import java.io.*;public class IODemo3 {//使用一下字节流来读取文件public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("d:/test.txt");while (true) {byte[] buffer = new byte[1024];int len = inputStream.read(buffer);System.out.println("len: " + len);if (len == -1) {break;}//此时读取的结果就被放到了 byte 数组中for (int i = 0; i < len; i++) {System.out.println(""+buffer[i]);}}inputStream.close();}
}

read 的第二个版本,需要调用者提前准备好一个数组,这里的传参操作,相当于是把刚才准备好的数组,交给 read 方法,让 read 方法内部针对这个数组进行填写。(此处的参数相当于 “输出型参数” )

上面这里给的数组长度是 1024 ,read就会尽可能的读取 1024 个字节,填到数组里。但是实际上,文件剩余长度是有限的,如果剩余长度超过 1024,此时 1024 个字节都会填满,返回值就是 1024了。如果当前剩余的长度不足 1024,此时有多少就填多少,read 方法就会返回当前实际读取的长度。

这里的len就表示read的返回值,也是我们读取的字节数量。如果此时数组满了那么就会返回 1024。剩余的72 101 108 108 111 这五个数字就是我们二进制所对应的ASCII对应的字符即 - --》 Hello

由上图可知,我们写入的字符的确是 “Hello” 哦!

buffer存在的意义,就是为了提高 IO 操作效率。

单次 IO 操作,是要访问硬盘/IO设备。单次操作时比较消耗时间的。

如果频繁进行这样的 IO 操作,耗时肯定就更多了。

单词 IO 时间是一定的,如果能缩短 IO 次数,此时就可以提高程序整体效率了。

第一个版本的代码是,是一次读1个字节,循环次数就比较高。read次数也很高。

第二个版本的代码,是一次读1024个字节,循环次数就降低了很多。read次数变少了。

buffer - 缓冲区,缓和了一下冲突,减少冲击的次数。

3.3.2示例2

这里我们把文件内容中填充中文看看,注意,写中文的时候使用 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);}}}}
}

3.4利用 Scanner 进行字符读取

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

3.4.1示例1

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);}}}}
}

此时,内部的 inputStream 对象已经被 try() 关闭了。里面的这个 Scanner,不关闭,也没事。

3.5OutputStream 概述 - 写文件(字节流)

3.5.1方法

这里的 close 操作,含义是,关闭文件。

那么问题来了,如果没写这个 close 操作,会怎么样呢?

答:如果没有close,对应的表项,没有及时释放。虽然 Java 有 GC,GC操作(垃圾回收)会在回收这个 outputStream 对象的时候去完成这个释放操作,但是这个 GC 不一定及时…

所以,如果不手动释放,意味着文件描述符表可能很快就被占满了。(这个数组,不能自动扩容,存在上限的!!!)

如果占满了之后,后面再次打开文件,就会打开失败!!!

文件描述符表最大长度,不同系统上不太一样,但是基本就是 几百个到几千个左右。因此如果忘记了释放操作符表的话我们的操作符表很快就会被占满,占满后我们后续就不能正常打开文件了。

那么如何确保我们会写上close呢?

示例代码一:

        OutputStream outputStream = null;try {outputStream = new FileOutputStream("d:/test.txt");outputStream.write(97);outputStream.write(98);outputStream.write(99);outputStream.write(100);} finally {outputStream.close();}

finally代码块用于代码后面,不管你前面的代码究竟发生了什么,都会执行finally代码

通过try finally的方式可以很好的起到提醒我们要close!

示例代码二:

        try (OutputStream outputStream = new FileOutputStream("d:/test.txt")) {outputStream.write(97);outputStream.write(98);outputStream.write(99);outputStream.write(100);}

这个是更推荐的写法!

这个写法虽然没有显示的写close,实际上是会执行的。只要 try 语句块执行完毕,就可以自动执行到 close !!!

3.5.2说明

OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中, 所以使用 FileOutputStream

3.6利用 OutputStreamWriter 进行字符写入

3.6.1示例1

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;public class IODemo4 {//进行写文件public static void main(String[] args) throws IOException {OutputStream outputStream = new FileOutputStream("d:/test.txt");outputStream.write(97);outputStream.write(98);outputStream.write(99);outputStream.write(100);outputStream.close();}
}

test.txt原本内容:

test.txt修改之后内容:

通过这个可以看到,虽然我们idea编译器什么都没有打印,但是它执行完成之后,我们文本文件里面已经删除了原来内容并添加进去了我们自己的新内容:abcd

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');// 不要忘记 flushos.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);// 不要忘记 flushos.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);// 不要忘记 flushos.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);// 不要忘记 flushos.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);// 不要忘记 flushos.flush();}}
}

3.7利用 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();

3.7.1示例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();}}}}
}

3.8为什么InputStream是读文件而OutputStream是写文件?

按照我们一般的逻辑思维我们确实会认为input是输入的意思,那么input就是写。而output是输出的意思,那么output就是读取操作。

但是在这里我们是以 CPU 为中心的!

数据朝着 CPU 的方向流向,就是输入。所以就把 数据从硬盘到内存 这个过程就称为读,input。

数据远离 CPU 的方向流向,就是输出。所以就把 数据从内存到硬盘 这个过程就称为写,output。

3.9Reader 和 Writer (字符流)

3.9.1Reader

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;public class IODemo5 {public static void main(String[] args) {try (Reader reader = new FileReader("d:/test.txt")){while (true) {int ch = reader.read();if (ch == -1) {break;}System.out.print((char)ch + " ");}} catch (IOException e) {throw new RuntimeException(e);}}
}

此时test.txt内容:

从test.txt不难看出,我们Reader读取是正确的!!!

3.9.2Writer

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;public class IODemo6 {public static void main(String[] args) {try (Writer writer = new FileWriter("d:/test.txt")){writer.write("hello world");}catch (IOException e) {e.printStackTrace();}}
}

随着程序运行结束我们来看一下运行之后,我们test.txt变成了什么样子呢?

首先我们看一下test.txt的初始状态:

程序运行结束后test.txt的数据:

我们test.txt的值修改为了 hello world 程序运行成功了!!!

3.10 字节 和 字符的区别

IO - 字节流(byte)

InputStream

OutputStream

IO - 字符流

Reader

Writer

补充小知识:

4.小程序练习

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

4.1示例1

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

类似于上图的这个功能

完整代码:

import java.io.*;
import java.util.Scanner;public class IODemo8 {private static Scanner scanner = new Scanner(System.in);public static void main(String[] args) {//让用户输入一个指定搜索的目录//Scanner scanner = new Scanner(System.in);System.out.println("请输入要搜索的路径");String basePath = scanner.next();//针对用户输入进行一个简单判定File root = new File(basePath);if (!root.isDirectory()) {//路径不存在,或者只是一个普通文件,此时无法进行搜索System.out.println("当前输入有误!");return;}//再让用户输入一个要删除的文件名System.out.println("请输入要删除的文件名");//此处要使用 next,请不要使用nextLine!!!String nameToDelete = scanner.next();//针对指定路径进行扫描,递归操作。//从根目录触发。(root)//先判定一下,在当前的目录里,是否包含咱们要删除的文件,如果是,就删除,否则就跳过下一个。//如果当前这里包含了一些目录,再针对子目录进行递归。scanDir(root,nameToDelete);}private static void scanDir(File root, String nameToDelete) {System.out.println("[scanDir]" + root.getAbsolutePath());//1.列出当前路径下包含的内容File[] files = root.listFiles();if (files == null) {//当前 root 目录下没东西,是一个空目录//结束继续递归return;}//2.遍历当前列出的结果for (File f: files) {if (f.isDirectory()) {//如果是目录,就进一步递归scanDir(f,nameToDelete);} else {//如果是普通文件,则判定是否要删除if (f.getName().contains(nameToDelete)) {System.out.println("是否确认要删除 " + f.getAbsolutePath() + "这个文件?");String choice = scanner.next();if (choice.equals("y") || choice.equals("Y")) {f.delete();System.out.println("删除成功!");} else {System.out.println("删除取消!");}}}}}
}

删除前:

删除时代码运行结果图:

删除后:

直到这里我们可以证明我们删除指定文件代码成功啦!!!

下面是我们针对上述完整代码的一些解析:

相当于是看一下,当前这个目录里有啥。好比,文件资源管理器双击了一个目录打开一样。

文件资源管理器中显示的这些结果,就相当于 listFiles 得到的内容。

这块代码涉及到递归遍历目录的操作。(经典操作/经典面试题)

4.2示例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("复制已完成");}
}

拷贝前:

拷贝中代码运行结果图:

拷贝后:

下面是我们针对上述完整代码的一些解析:

try() 语法,支持包含多个流对象,多个流对象之间,使用;分隔开就行了。

无论是什么都可以拷贝,word.txt.jpg 均是可以拷贝的

4.3示例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;}
}

5.代码参考

5.1如何按字节进行数据读

try (InputStream is = ...) {byte[] buf = new byte[1024];while (true) {int n = is.read(buf);if (n == -1) {break;}// buf 的 [0, n) 表示读到的数据,按业务进行处理}
}

5.2如何按字节进行数据写

try (OutputStream os = ...) {byte[] buf = new byte[1024];while (/* 还有未完成的业务数据 */) {// 将业务数据填入 buf 中,长度为 nint n = ...;os.write(buf, 0, n);}os.flush(); // 进行数据刷新操作
}

5.3如何按字符进行数据读

try (InputStream is = ...) {try (Scanner scanner = new Scanner(is, "UTF-8")) {while (scanner.hasNextLine()) {String line = scanner.nextLine();// 根据 line 做业务处理}}
}

5.4如何按字符进行数据写

try (OutputStream os = ...) {try (OutputStreamWriter osWriter = new OutputStreamWriter(os, "UTF-8")) {try (PrintWriter writer = new PrintWriter(osWriter)) {while (/* 还有未完成的业务数据 */) {writer.println(...);}writer.flush(); // 进行数据刷新操作}}
}

感谢各位读者的阅读,本文章有任何错误都可以在评论区发表你们的意见,我会对文章进行改正的。如果本文章对你有帮助请动一动你们敏捷的小手点一点赞,你的每一次鼓励都是作者创作的动力哦!😘

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/68368.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Program Evaluation and Review Technique(PERT)

Program Evaluation and Review Technique&#xff08;PERT&#xff09;计划评估和审查技术 大多数的时候&#xff0c;我不太记得概念&#xff0c;大致是这么回事&#xff0c;为了方便记忆&#xff0c;从小到大 1)beta distribution 贝塔分布&#xff08;成本&#xff0c;也可以…

【计算机视觉 | Kaggle】保姆级教程:入门 Kaggle 的步骤详细介绍

文章目录 一、Overview二、Evaluation三、Timeline四、Code Requirements五、Data5.1 数据的可视化5.2 文件 六、Discussion七、Code 一、Overview 当进入到一场比赛的 Overview 页面后&#xff0c;先读完 Description&#xff0c;了解比赛讲了一件什么事情。 我们以一场比赛…

ASR 语音识别接口封装和分析

这个文档主要是介绍一下我自己封装了 6 家厂商的短语音识别和实时流语音识别接口的一个包&#xff0c;以及对这些接口的一个对比。分别是&#xff0c;阿里&#xff0c;快商通&#xff0c;百度&#xff0c;腾讯&#xff0c;科大&#xff0c;字节。 zxmfke/asrfactory (github.c…

第三篇|金融人数据来源有哪些

数据对于金融行业真的很重要&#xff0c;那么金融人有哪些途径查数据呢&#xff1f; 国内&#xff1a; 1. 国家统计局 这个应该是无论什么行业都使用最频繁的网站&#xff0c;每个月都会固定发上个月资产投资数据 、工业增加值和利润数据等常规数据&#xff0c;其他数据也会…

编程小白的自学笔记十三(python办公自动化读写文件)

系列文章目录 编程小白的自学笔记十二&#xff08;python爬虫入门四Selenium的使用实例二&#xff09; 编程小白的自学笔记十一&#xff08;python爬虫入门三Selenium的使用实例详解&#xff09; 编程小白的自学笔记十&#xff08;python爬虫入门二实例代码详解&#xff09;…

每天一道leetcode:712. 两个字符串的最小ASCII删除和(动态规划中等)

今日份题目&#xff1a; 给定两个字符串s1 和 s2&#xff0c;返回 使两个字符串相等所需删除字符的 ASCII 值的最小和 。 示例1 输入: s1 "sea", s2 "eat" 输出: 231 解释: 在 "sea" 中删除 "s" 并将 "s" 的值(115)加入…

【uniapp】uniapp自动导入自定义组件和设置分包:

文章目录 一、自动导入自定义组件&#xff1a;二、设置分包和预加载&#xff1a; 一、自动导入自定义组件&#xff1a; 【Volar 官网】https://github.com/vuejs/language-tools 二、设置分包和预加载&#xff1a; 【官方文档】https://uniapp.dcloud.net.cn/collocation…

19. 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点 题目-中等难度示例1. 统计节点数删除节点 题目-中等难度 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,…

React源码解析18(4)------ completeWork的工作流程【mount】

摘要 经过上一章&#xff0c;我们得到的FilberNode已经具有了child和return属性。一颗Filber树的结构已经展现出来了。 那我们最终是想在页面渲染真实的DOM。所以我们现在要在completeWork里&#xff0c;构建出一颗离屏的DOM树。 之前在说FilberNode的属性时&#xff0c;我们…

3.1 Qt样式选择器

本期内容 3.1 样式选择器 3.1.1 Universal Selector (通用选择器) 3.1.2 Type Selector (类型选择器) 3.1.3 Property Selector (属性选择器) 3.1.4 Class Selector (类选择器) 3.1.5 ID Selector (ID选择器) 3.1.6 Descendant Selector (后裔选择器) 3.1.7 Chil…

Python-OpenCV中的图像处理-模板匹配

Python-OpenCV中的图像处理-模板匹配 模板匹配单对象的模板匹配多对象的模板匹配 模板匹配 使用模板匹配可以在一幅图像中查找目标函数&#xff1a; cv2.matchTemplate()&#xff0c; cv2.minMaxLoc()模板匹配是用来在一副大图中搜寻查找模版图像位置的方法。 OpenCV 为我们提…

【数据库】Redis可以替代Mysql吗

Redis和Mysql的搭配 Redis可以替代Mysql吗什么是RedisRedis适用的场景以及优点Redis的缺点 什么是MysqlMysql的优点Mysql缺点 总结 Redis可以替代Mysql吗 Redis不能代替MySQL&#xff0c; Redis和MySQL只能是一种互补。 什么是Redis Redis是一种非关系型数据库&#xff0c;也…