当前位置: 首页 > article >正文

JAVA如何操作文件?(超级详细)

目录

一、认识文件和相关知识

1.认识文件

2.⽬录

3.⽂件路径(Path) 

4.文本文件和二进制文件的区分 

二、File类操作文件 

1.构造方法 

2.方法

2.1 方法表

2.2  get相关的方法和构造方法

2.2.1 “.” 和 “..”

 2.3 is相关的方法 

2.4 删除相关的方法 

2.5 返回当前目录下的文件

2.5.1 递归实现遍历路径下所有文件 

 2.6 创建目录

2.7 renameTo()

三、流 (Stream)

1.初时“流”

2.输入输出的角度

3.字节流的操作

3.1 InputStream

3.1.1 FileInputStream

3.1.1.1 read()

3.1.1.2 read(byte[] b)

3.1.1.3 read(byte[] b,int off,int len)

3.1.1.4 Scanner获取文件内容

3.2 OutputStream

3.2.1 FileOutputStream

3.2.1.1 write(int b)

 3.2.1.2 write(byte[] b)

3.2.1.3 write(byte[] b, int off, int len)

4.字符流的操作

4.1 Reader

4.1.1 FileReader

4.1.1.1 read() 

4.1.1.2 read(char[] cbuf)

4.1.1.3 read(char[] cbuf,int off,int len)

4.2 Writer

4.2.1FileWriter

4.2.1.1 write(String str)

4.2.1.2 write(char cbuf[])

4.2.1.3 write(char cbuf[], int off,int len)

四、案例练习

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

2.复制普通文件

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


一、认识文件和相关知识

1.认识文件

我们先来认识狭义上的⽂件(file)。针对硬盘这种持久化存储的I/O设备,当我们想要进⾏数据保存时,往往不是保存成⼀个整体,⽽是独⽴成⼀个个的单位进⾏保存,这个独⽴的单位就被抽象成⽂件的概念,就类似办公桌上的⼀份份真实的⽂件⼀般。
简单来说就是,文件是保存在硬盘上的,但是硬盘非常大,硬盘就用逻辑抽象成一个文件
⽂件除了有数据内容之外,还有⼀部分信息,例如⽂件名、⽂件类型、⽂件⼤⼩等并不作为⽂件的数据⽽存在,我们把这部分信息可以视为⽂件的元信息。

2.⽬录

同时,随着⽂件越来越多,对⽂件的系统管理也被提上了⽇程 ,如何进⾏⽂件的组织呢,⼀种合乎⾃ 然的想法出现了,就是按照层级结构进⾏组织⸺也就是我们数据结构中学习过的树形结构。这样,⼀种专⻔⽤来存放管理信息的特殊⽂件诞⽣了,也就是我们平时所谓⽂件夹(folder)或者⽬录(directory)的概念。同时也可以看出,数据结构是n叉树

3.⽂件路径(Path) 

如何在⽂件系统中如何定位我们的⼀个唯⼀的⽂件就成为当前要解决的问题,但这难不倒计算机科学家,因为从树型结构的⻆度来看,树中的每个结点都可以被⼀条从根开始,⼀直到达的结点的路径所描述,⽽这种描述⽅式就被称为⽂件的绝对路径(absolute path)。
例如我要查找这个文件:
绝对路径:C:\Program Files\Java\jdk-11
除了可以从根开始进⾏路径的描述,我们可以从任意结点出发,进⾏路径的描述,⽽这种描述⽅式就被称为相对路径(relative path),相对于当前所在结点的⼀条路径。
相对路径,结合之后的代码讲解

4.文本文件和二进制文件的区分 

我们其实就可以通过记事本打开的方式查看。

  • 如果打开是我们认识的一些东西,包括我们写的.java文件,它大概率就是文本文件
  • 如果通过记事本打开的文件是一堆乱码,也就是二进制文件

所有的文件存储都是二进制,哪怕是文本文件,它底层存储的仍然是二进制,那为什么是我们可以看懂的文字?

因为文本文件会有一个查表的动作,这个“表”是把对应的二进制转换为字符,例如:ASII码表、UTF8、Unicode等

二、File类操作文件 

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

1.构造方法 

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

注意!这里创建的是实例,并不代表一定有这个文件存在

2.方法

2.1 方法表

修饰符及返回值类型
⽅法签名
说明
String
getParent()
返回 File 对象的⽗⽬录⽂件路径
String
getName()
返回 FIle 对象的纯⽂件名称
String
getPath()
返回 File 对象的⽂件路径
String
getAbsolutePath()
返回 File 对象的绝对路径
String
getCanonicalPath()
返回 File 对象的修饰过的绝对路径
boolean
exists()
判断 File 对象描述的⽂件是否真实
存在
boolean
isDirectory()
判断 File 对象代表的⽂件是否是⼀
个⽬录
boolean
isFile()
判断 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)
进⾏⽂件改名,也可以视为我们平
时的剪切、粘贴操作

2.2  get相关的方法和构造方法

    public static void main(String[] args) throws IOException {//打开我们工程目录下的文件File file = new File("E:\\Code\\lear-java\\io\\src");//或者可以使用://        File file = new File("E:/Code/lear-java/io");//返回File对象的父目录文件路径System.out.println(file.getParent());//返回File对象的纯文件名称System.out.println(file.getName());//返回文件路径System.out.println(file.getPath());//返回File对象的绝对路径System.out.println(file.getAbsolutePath());//返回File修饰过的路径System.out.println(file.getCanonicalPath());}

 

这样看后面三种没有区别,在使用相对地址的时候才可以区分出

2.2.1 “.” 和 “..”
  • .  :表示当前目录
  • .. : 表示上一级目录 或者 表示父级目录
    public static void main(String[] args) throws IOException {//打开当前目录File file = new File("./Test.txt");//返回File对象的父目录文件路径System.out.println(file.getParent());//返回File对象的纯文件名称System.out.println(file.getName());//返回文件路径System.out.println(file.getPath());//返回File对象的绝对路径System.out.println(file.getAbsolutePath());//返回File修饰过的路径System.out.println(file.getCanonicalPath());}

那么当前目录又是哪里?

程序默认在idea工程中运行的,那么当前文件就是在当前工程目录下。

例如:我的工程名为《IO》那么就在IO工程下:

结果:

而且我们的Test.txt文件根本就不存在,所以就算你获取到了路径、名字等等,也不能代表文件是真实存在的。

    public static void main(String[] args) throws IOException {//父级目录File file = new File("../Test.txt");//返回File对象的父目录文件路径System.out.println(file.getParent());//返回File对象的纯文件名称System.out.println(file.getName());//返回文件路径System.out.println(file.getPath());//返回File对象的绝对路径System.out.println(file.getAbsolutePath());//返回File修饰过的绝对路径System.out.println(file.getCanonicalPath());}

结果:

 2.3 is相关的方法 

当文件不存在:

    public static void main(String[] args) {//此时这个文件并不存在File file = new File("./Test.txt");//判断文件是真实否存在System.out.println(file.exists());//判断文件是否是一个目录System.out.println(file.isDirectory());//判断文件是否是一个普通文件System.out.println(file.isFile());}

结果
 

此时我们加上创建文件的方法

    public static void main(String[] args) throws IOException {//此时这个文件并不存在File file = new File("./Test.txt");//创建文件System.out.println(file.createNewFile());//判断文件是真实否存在System.out.println(file.exists());//判断文件是否是一个目录System.out.println(file.isDirectory());//判断文件是否是一个普通文件,二进制文件也算是普通文件System.out.println(file.isFile());}

结果:

  • 当文件存在,不会创建,返回false
  • 当文件不存在,创建文件,返回true
  • 创建失败抛出异常IOException

什么情况下会创建失败?

1. 硬盘空间不够了

在日常我们使用的电脑,一般来说不容易遇到这个问题,但是在以后的工作中可能就比较常见,因为每天都会产生大量的日志,会产生新的文件,这个时候就会创建失败抛出异常IOException。

2.没有权限

主要是读和写的权限,如果你只要读的权限去写,肯定也是不行的

3.父目录不存在

写入的目录路径错误,根本就没有这个目录

4.其他I/O错误

如文件路径被占用、硬件故障……

2.4 删除相关的方法 

    public static void main(String[] args) throws IOException {File file = new File("./Test.txt");//创建文件System.out.println(file.createNewFile());//删除文件,删除成功返回trueSystem.out.println(file.delete());}

结果:

除了马上删除,还可以让文件在线程结束的时候删除:

    public static void main(String[] args) throws IOException {File file = new File("./Test.txt");//先创建文件System.out.println(file.createNewFile());//当进程结束的时候才会删除文件file.deleteOnExit();System.out.println("是否要结束进程");//利用Scanner造成阻塞Scanner sc = new Scanner(System.in);sc.next();System.out.println("进程结束");}

使用场景之一:

我们在word创建一个文档且还未保存的时候,它会创建一个临时文件用来保存,你正在编辑的内容。如果程序异常关闭了,就可以通过这个文件恢复之前编辑的内容。

2.5 返回当前目录下的文件

    public static void main(String[] args) {File file = new File(".");String[] s = file.list();for (int i = 0; i < s.length; i++) {System.out.println(s[i]);}}

结果:

打印出来的顺序并不和我们文件中看到的顺序相同

这个方法功能比较有限,我们一般使用listFiles()方法比较多,可以直接获取到目录下每个文件的实例

    public static void main(String[] args) {File file = new File(".");//获取该文件下,所有对象的实例File[] fs = file.listFiles();for (int i = 0; i < fs.length; i++) {System.out.println(fs[i].getName());}}

我们也可以通过这个方法,打印出当前目录下文件的名字。

但是!我们src也是一个目录,目录下的文件并不能通过这样的方式简单打印出来,

就需要使用递归的方法。在此之前我们需要知道这个方法的几种情况:

  1. 当遇到的文件不是目录,返回null
  2. 路径错误,返回null
  3. 如果是一个空目录,就返回一个空数组
2.5.1 递归实现遍历路径下所有文件 

 我们现在可以来实现一下:

    private static void scan(File file) throws IOException {//如果不是目录,就退出if (!file.isDirectory()) {return;}//是目录就获取,目录中每个文件的对象File[] fs = file.listFiles();//   如果是空目录    或者   不存在的路径if (fs.length == 0 || fs == null) {return;}for (int i = 0; i < fs.length; i++) {//如果是普通文件就打印if (fs[i].isFile()) {System.out.println(fs[i].getCanonicalPath());}else {//到这里肯定就是目录了//就先打印目录名,再继续递归System.out.println(fs[i].getName());scan(fs[i]);}}}public static void main(String[] args) {File file = new File(".");try {scan(file);} catch (IOException e) {e.printStackTrace();}}

结果:

 2.6 创建目录

    public static void main(String[] args) {File file = new File("./aaa");//创建一个目录,如果创建成功返回true,否则falseboolean ok = file.mkdir();System.out.println(ok);}

 结果:

那如果在有这个目录的情况下,我们再次运行,会创建成功吗?

直接返回了false创建失败了,说明它会先检查一下是否有同名的文件夹。

那么我们是否能同时创建,多级目录呢?

​public static void main(String[] args) {File file = new File("./bb/cc/dd");//创建一个目录,如果创建成功返回true,否则falseboolean ok = file.mkdir();System.out.println(ok);}​

结果:

并不能同时创建多级目录,但是我们还有一个方法是可以创建多级目录的:

    public static void main(String[] args) {File file = new File("./bb/cc/dd");//创建一个目录,如果有必要会创建多级目录,如果创建成功返回true,否则falseboolean ok = file.mkdirs();System.out.println(ok);}

结果:

那么用mkdirs()方法就可以创建多级目录了

2.7 renameTo()

改名操作:

    public static void main(String[] args) {File file1 = new File("./Test.txt");File file2 = new File("./Test2.txt");//改名或者移动成功返回true,反之falseboolean ok = file1.renameTo(file2);System.out.println(ok);}

结果:

移动操作,需要最后名字写为相同的:

    public static void main(String[] args) {//现在我们想将,之前改名的Test2.txt,移动到之前创建的aaa文件夹中File file1 = new File("./Test2.txt");File file2 = new File("./aaa/Test2.txt");//改名或者移动成功返回true,反之falseboolean ok = file1.renameTo(file2);System.out.println(ok);}

 结果:

三、流 (Stream)

1.初时“流”

关于文件的操作,都是系统提供了API,java已经帮我们封装好了。

我们把这些封装好的类统称为,《文件流》或者《IO流》

流是一个形象的比喻,例如:

水流:

通过水龙头,接100ml水

  1. 直接一口气100ml接完
  2. 一次接50ml,分两次接完
  3. 一次接10ml,分10次接完
  4. 一次接1ml,分100次接完
  5. ……

文件流/IO流:

我要从文件读取100字节的数据

  1. 直接一口气,把100字节的数据读完
  2. 一次读50字节,分两次
  3. 一次读10字节,分10次
  4. 一次读1字节,分100次
  5. ……

java实现IO流的类有很多,分成两个大类

1. 字节流(二进制):读写数据的基本单位,就是字节,实现的主要接口:

  • InputStream(输入)
  • OutputStream(输出)

2. 字符流:读写数据的基本单位,就是字符

  • Writer(输入)
  • Reader(输出)

上述四个类,都是“抽象类”,真正干活的是下面的一堆继承它的类。

实现了这么多类怎么区分字符流和IO流呢

凡是后缀带有 InputStream 或者 OutputStream 就是字节流

凡是后缀带有 Wirte 或者 Reader

我们只需要去了解常用的类就可以了。

2.输入输出的角度

我们从CPU的角度出发:

  • 去硬盘上写数据,就是写
  • 去硬盘上读数据,就是读

3.字节流的操作

3.1 InputStream

常用的方法

修饰符及返回值类型方法签名说明
intread()读取一个字节的数据,返回-1代表已经读完了
int read(byte[] b)最多读取b.length字节的数据到b中, 返回实际读到的数量;-1代表以及读完了
intread(byte[] b,int off,int len)将从文件读取的数据放入到数组b中,off:代表开始存储数组的下标 ,len:代表最多读取的字节个数。返回读取到的字节个数,如果读到末尾返回-1
voidclose()关闭字节流
InputStream 只是⼀个抽象类,要使⽤还需要具体的实现类。关于 InputStream 的实现类有很多,基本可以认为不同的输⼊设备都可以对应⼀个 InputStream 类,我们现在只关⼼从⽂件中读取,所以使⽤ FileInputStream
3.1.1 FileInputStream
    public static void main(String[] args) throws FileNotFoundException {//打开字节流并,打开文件                     这个地址可以是不存在的InputStream inputStream = new FileInputStream("./Test.txt");}

这样的一段代码,它会抛这样一个异常:FileNotFoundException,表示程序尝试打开或访问指定路径的文件时失败。它是IOExciption的一种特殊情况。

如果没有这个文件,就会抛出此异常:

这个语句还隐含了“打开文件”这样的操作既然有打开就有关闭。

    public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("./Test.txt");//关闭字节流inputStream.close();}

如果不关闭,可能会造成程序崩溃:

打开文件,其实是在该进程中的 “文件描述符” 中,创建了一个新的表项。

文件描述符:描述了该进程都要操作哪些文件。数组的每个元素就是一个struct file对象,每个结构体就描述了对应的文件信息,数组的小标就称为“文件描述符”。

每次打开一个文件,就想当于在数组上占用了一个位置,而这个数组又是不能扩容的,如果数组满了就会打开文件失败。除非主动调用close才会关闭文件,或者这个进程直接结束了这个数组也被带走了。

但是难免会存在一些情况导致close()执行不到,例如:在执行到之前抛出了异常、遇到了return。所以我们应该把close()方法放到finally中:

    public static void main(String[] args) {InputStream inputStream = null;try {inputStream = new FileInputStream("./Test.txt");} catch (FileNotFoundException e) {e.printStackTrace();} finally {try {//关闭inputStream.close();} catch (IOException e) {e.printStackTrace();}}}

但是这样写感觉非常的繁琐,那我们其实还有一种解决方式:

    public static void main(String[] args) {//写在这里,就不用加close()了。只有实现了Closeable的语句才可以写在这里try (InputStream inputStream = new FileInputStream("./Test.txt")) {} catch (IOException e) {}

写在try()里的必须是实现了Closeable接口的,这样的写法是非常推荐的:

try()里支持放入多个对象,使用“;”分割

3.1.1.1 read()

每次读取一个字节的内容,读值只会返回0 - 255的数据,如果读取到了末尾返回-1。

 创建一个文件Test.txt在里面写上hello:

    public static void main(String[] args) {try (InputStream inputStream = new FileInputStream("./Test.txt")) {while (true) {int b = inputStream.read();if (b == -1) {//如果等于-1就代表读到了文件末尾return;}System.out.printf("0x%x = %c \n",b,b);}} catch (IOException e) {e.printStackTrace();}}

结果:

一次读取太慢了,而且每去读一次,就会执行一次IO操作,IO操作的开销是比较大的,我们可以使用数组参数的read()。此方法虽然还是一个个在读到数组中的,但提升比较大的原因是节省了每次的IO操作开销

3.1.1.2 read(byte[] b)

将读取到的参数,传入到数组b中。如果没有读取到末尾,就返回每次读取到的字节数,到了末尾返回-1

    public static void main(String[] args) {try (InputStream inputStream = new FileInputStream("./Test.txt")) {//防止数据一次读不完while (true) {byte[] buffer = new byte[1024];//n代表读取到了多少个字节,读取到末尾仍然返回-1int n = inputStream.read(buffer);if (n == -1) {//如果等于-1就代表读到了文件末尾return;}for (int i = 0; i < n; i++) {System.out.printf("0x%x -> %c\n",buffer[i],buffer[i]);}}} catch (IOException e) {e.printStackTrace();}}

 结果:

3.1.1.3 read(byte[] b,int off,int len)
将从文件读取的数据放入到数组b中,off:代表开始存储数组的下标 ,len:代表最多读取的字节个数。返回读取到的字节个数,如果读到末尾返回-1
    public static void main(String[] args) {try (InputStream inputStream = new FileInputStream("./Test.txt")) {byte[] buffer = new byte[1024];//n代表读取到了多少个字节,读取到末尾仍然返回-1//off:代表偏移量,此处写的1,就是从数组为1的位置开始写//len:表示最多读取的字节个数int n = inputStream.read(buffer,1,3);if (n == -1) {//如果等于-1就代表读到了文件末尾return;}for (int i = 1; i < n+1; i++) {System.out.printf("0x%x -> %c\n",buffer[i],buffer[i]);}} catch (IOException e) {e.printStackTrace();}}

 结果:

3.1.1.4 Scanner获取文件内容
Scanner sc = new Scanner(System.in);

在这之前,我们可能一直不知道System.in是什么参数,我们点进去看一下:

public static final InputStream in = null;

我们发现他就是一个InputStream,那我们传入的参数就可以是InputStream对象。

 我们现在文件中是这样的内容:

    public static void main(String[] args) {try (InputStream inputStream = new FileInputStream("./Test.txt")) {Scanner sc = new Scanner(inputStream);//判断下一个是否为Int类型while (sc.hasNextInt()) {System.out.println(sc.nextInt());}} catch (IOException e) {e.printStackTrace();}}

结果:

如果里面的内容被其他类型则会中断:

    public static void main(String[] args) {try (InputStream inputStream = new FileInputStream("./Test.txt")) {Scanner sc = new Scanner(inputStream);//判断下一个是否为Int类型while (sc.hasNextInt()) {System.out.println(sc.nextInt());}} catch (IOException e) {e.printStackTrace();}}

结果:

这个时候用字符串类型可能就比较好

如果是字符串

    public static void main(String[] args) {try (InputStream inputStream = new FileInputStream("./Test.txt")) {Scanner sc = new Scanner(inputStream);//判断下一个是否为String类型while (sc.hasNext()) {System.out.println(sc.next());}} catch (IOException e) {e.printStackTrace();}}

结果:

3.2 OutputStream

修饰符及返回值类型
⽅法签名
说明
void
write(int b)
写⼊要给字节的数据
void
write(byte[] b)
将 b 这个字符数组中的数据全部写
⼊ os 中
int
write(byte[] b, int off, int len)
将 b 这个字符数组中从 off 开始的
数据写⼊ os 中,⼀共写 len 个
OutputStream 同样只是⼀个抽象类,要使⽤还需要具体的实现类。我们现在还是只关⼼写⼊⽂件中,所以使⽤ FileOutputStream
3.2.1 FileOutputStream

依然包含打开文件这样的隐藏操作,所以也需要关闭字节流。打开失败依然会抛出FileNotFoundException。如果文件不存在会自动创建文件,但是如果目录都不存在会抛出异常

3.2.1.1 write(int b)

每次输入一个字节b,如果写入失败会抛出IOException

现在将Test.txt文件里的内容清空

    public static void main(String[] args) {//写文件,依然包含打开文件这样的隐藏操作,打开失败依然会抛出FileNotFoundExceptiontry (OutputStream outputStream = new FileOutputStream("./Test.txt")) {//每次写入一个字节outputStream.write(0x68);//houtputStream.write(0x65);//eoutputStream.write(0x6c);//loutputStream.write(0x6c);//loutputStream.write(0x6f);//o} catch (IOException e) {e.printStackTrace();}}

结果: 

 

但是如果我们再次运行:

在我们创建这个对象的时候它回清空文件中的数据,再开始写

那如果我们不想让它清空数据,就在实例化对象的时候填入一个true

OutputStream outputStream = new FileOutputStream("./Test.txt",true)

这个时候就不会清空,文件中的数据了

 3.2.1.2 write(byte[] b)

 将数组b的内容写到文件中,如果写入失败会抛出IOException

 我们用此方法写“你好”:

    public static void main(String[] args) {try (OutputStream outputStream = new FileOutputStream("./Test.txt",true)) {//此数组中存放的是,“你好”的二进制数据//必须进行强制类型转换,不然会报错byte[] buffer = {(byte) 0xe4,(byte) 0xbd,(byte) 0xa0,(byte) 0xe5,(byte) 0xa5,(byte) 0xbd};outputStream.write(buffer);} catch (IOException e) {e.printStackTrace();}}

 结果:

3.2.1.3 write(byte[] b, int off, int len)

b数组做为数据源,表示要写入的内容,

off:代表从b数组中哪一个元素开始写入

len:表示最多写入多少个字节

    public static void main(String[] args) {try (OutputStream outputStream = new FileOutputStream("./Test.txt",true)) {byte[] buffer = {(byte) 0xe4,(byte) 0xbd,(byte) 0xa0,(byte) 0xe5,(byte) 0xa5,(byte) 0xbd};//将buffer数组的数据,从0下标开始,输入6个字节的数据outputStream.write(buffer,0,6);} catch (IOException e) {e.printStackTrace();}}

结果:

这个时候如果我们乱输入一个数据范围:

    public static void main(String[] args) {try (OutputStream outputStream = new FileOutputStream("./Test.txt",true)) {byte[] buffer = {(byte) 0xe4,(byte) 0xbd,(byte) 0xa0,(byte) 0xe5,(byte) 0xa5,(byte) 0xbd};outputStream.write(buffer,1,3);} catch (IOException e) {e.printStackTrace();}}

结果:这个适合它就不认识你写的什么东西了

如果超出了你数据源的范围:

    public static void main(String[] args) {try (OutputStream outputStream = new FileOutputStream("./Test.txt",true)) {byte[] buffer = {(byte) 0xe4,(byte) 0xbd,(byte) 0xa0,(byte) 0xe5,(byte) 0xa5,(byte) 0xbd};//从 2 下标开始,一共就只有4个数据了,超过了这个就会抛出异常outputStream.write(buffer,2,6);} catch (IOException e) {e.printStackTrace();}}

如果是文本文件的操作,更适合字符流的方式

4.字符流的操作

4.1 Reader

修饰符及返回值类型方法签名说明
intread()读取一个字符的数据,返回读取到的数据,但是是int类型,为了兼容读取到末尾返回-1,正常读取到数据的返回范围是0-65535。
intread(char[] cbuf)
 
将文件中读取到的字符,写到cbuf数组中。返回的是读取到字符的个数,如果读取到文件末尾,则返回-1
intread(char[] cbuf, int off, int len)将在文件读取到的内容返回到cbuf数组中,off:从数组中的哪一个下标开始写,len表示最多读取的字符个数。返回读取到的个数,如果是读取到文件末尾就返回-1
4.1.1 FileReader

这个类和上面讲述的InputStream差不多,我就不过多讲述了

4.1.1.1 read() 

读取一个字符的数据,返回读取到的数据,但是是int类型,为了兼容读取到末尾返回-1,正常读取到数据的返回范围是0-65535。如果文件不存在会自动创建文件,但是如果目录都不存在会抛出异常

    public static void main(String[] args) {try (Reader reader = new FileReader("./Test.txt")) {while (true) {//读取一个字符的内容,但是返回的是int类型,为了兼容-1int c = reader.read();if (c == -1) {//读取到了文件末尾return;}char ch = (char) c;System.out.println(ch);}} catch (IOException e) {e.printStackTrace();}}

结果:

可能熟悉二进制范围的朋友就有疑问了,utf8汉字存储用了3个字节,java中的char类型是2给字节,怎么能装得下的。其实java中char类型进行了转码,转为了Unicode编码。

4.1.1.2 read(char[] cbuf)

将文件中读取到的字符,写到cbuf数组中。返回的是读取到字符的个数,如果读取到文件末尾,则返回-1

    public static void main(String[] args) {try (Reader reader = new FileReader("./Test.txt")) {while (true) {char[] buffer = new char[1024];//返回读取到字符的个数,如果读到文件末尾返回-1int n = reader.read(buffer);if (n == -1) {return;}for (int i = 0; i < n; i++) {System.out.println(buffer[i]);}}} catch (IOException e) {e.printStackTrace();}}
4.1.1.3 read(char[] cbuf,int off,int len)

将在文件读取到的内容返回到cbuf数组中,off:从数组中的哪一个下标开始写,len表示最多读取的字符个数。返回读取到的个数,如果是读取到文件末尾就返回-1

    public static void main(String[] args) {try (Reader reader = new FileReader("./Test.txt")) {char[] buffer = new char[1024];//返回读取到字符的个数,如果读到文件末尾返回-1int n = reader.read(buffer,1,3);if (n == -1) {return;}for (int i = 1; i < n+1; i++) {System.out.println(buffer[i]);}} catch (IOException e) {e.printStackTrace();}}

结果:

4.2 Writer

修饰符及返回值类型方法签名说明
voidwrite(String str)将str中的数据写入文件中
voidwrite(char cbuf[])将cbuf数组中的值,写入到文件中
voidwrite(char cbuf[], int off,int len)将cbuf数组做为数据源,off(偏移量)做为从哪个数组下标开始写,len表示最多写多少个字符。
4.2.1FileWriter

这个类如果不在形参中写true,每次实例化对象的时候也会把文件清空

4.2.1.1 write(String str)

这个方法可以用Stirng传入就非常方便了

    public static void main(String[] args) {try (Writer writer = new FileWriter("./Test.txt")) {writer.write("你好世界");} catch (IOException e) {e.printStackTrace();}}

结果:

把之前的两个你好给清空了

当我们加上true:

    public static void main(String[] args) {try (Writer writer = new FileWriter("./Test.txt",true)) {writer.write("你好世界");} catch (IOException e) {e.printStackTrace();}}

4.2.1.2 write(char cbuf[])

将cbuf数组中的值,写入到文件中

    public static void main(String[] args) {try (Writer writer = new FileWriter("./Test.txt",true)) {char[] buffer = {'哈','哈','哈'};writer.write(buffer);} catch (IOException e) {e.printStackTrace();}}

 结果:

4.2.1.3 write(char cbuf[], int off,int len)

将cbuf数组做为数据源,off(偏移量)做为从哪个数组下标开始写,len表示最多写多少个字符。

    public static void main(String[] args) {try (Writer writer = new FileWriter("./Test.txt",true)) {char[] buffer = {'啊','啊','啊','啊','啊','啊'};writer.write(buffer,1,4);} catch (IOException e) {e.printStackTrace();}}

结果:

四、案例练习

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

public class demo4 {//扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否//要删除该⽂件public static void main(String[] args) {//1.指定目录System.out.println("请输入你要查找的目录");Scanner sc = new Scanner(System.in);String rootPath = sc.next();File file = new File(rootPath);//2.判断地址是否为一个有效地址if (!file.isDirectory()) {//不是一个目录System.out.println("输入路径错位");return;}//3.获取要查找的关键字System.out.println("请输入你要查找的关键字");String key = sc.next();;//4.开始查找scan(file,key);}private static void scan(File file, String key) {//1.不是目录就不能继续递归了if (!file.isDirectory()) {return;}//2.获取当前目录下所有文件的对象File[] fs = file.listFiles();//3.防止发生IO错误 和 如果是一个目录if (fs == null || fs.length == 0) {return;}//4. 开始遍历for (File f : fs) {//5.如果是普通文件,判断是否有key值if (f.isFile()) {//7. 判断是否有key值toDelete(f,key);}else {//6.如果不是普通文件继续递归scan(f,key);}}}private static void toDelete(File f, String key) {Scanner sc = new Scanner(System.in);//获取文件名称是否包含key值if (f.getName().contains(key)) {//提示用户是否删除System.out.println("是否删除" + f.getName() + "文件");System.out.println("是Y/y 否N/n");String ch = sc.next();if (ch.equals("Y") || ch.equals("y")) {f.delete();}}}
}

要删除的目标:

输入的过程:

那么现在我们来看一下是否删除成功了:

2.复制普通文件

public class demo5 {//复制普通文件//读取二进制,并写入到目标文件public static void main(String[] args) {//1.输入要复制的文件Scanner sc = new Scanner(System.in);System.out.println("请输入你要复制的文件路径");String sourcePath = sc.next();File file = new File(sourcePath);//2.如果不是普通文件if (!file.isFile()) {System.out.println("你输入的文件路径有误");return;}//3.输入要复制的文件路径System.out.println("请输入要粘贴的文件路径");String targetPath = sc.next();File destFile = new File(targetPath);//4.目标文件的路径必须存在if (!destFile.getParentFile().isDirectory()) {System.out.println("目标文件的路径有误");return;}//5.开始复制//如果文件不存在,FileInputStream会自动创建文件try (InputStream inputStream = new FileInputStream(sourcePath);OutputStream outputStream = new FileOutputStream(targetPath)) {while (true) {byte[] buffer = new byte[1024];int n = inputStream.read(buffer);if (n == -1) {break;}//数据源不是每一次都能装满1024个字节outputStream.write(buffer,0,n);}} catch (IOException e) {e.printStackTrace();}}
}

 结果:

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

public class demo6 {//扫描指定⽬录,并找到内容中包含指定字符的所有普通⽂件(不包含⽬录)public static void main(String[] args) {Scanner sc = new Scanner(System.in);//1.输入要查找的目录System.out.println("请输入你要查找的目录");String path = sc.next();File file = new File(path);//2.如果不是目录if (!file.isDirectory()) {System.out.println("你输入的路径有误");return;}//3.输入查找词System.out.println("请输入你要查找的内容");String key = sc.next();//4.开始查找scan(file,key);}//5.开始递归遍历private static void scan(File file, String key) {//如果不是目录就不能递归了if (!file.isDirectory()) {return;}File[] fs = file.listFiles();if (fs.length == 0 || fs == null) {return;}for (File f: fs) {//如果是普通文件进行处理判断if (f.isFile()) {search(f,key);} else {//不是普通文件继续遍历scan(f,key);}}}//6.进行内容查询判断private static void search(File f, String key) {//用这个容器来接收所有数据StringBuilder stringBuilder = new StringBuilder();try (Reader reader = new FileReader(f)) {char[] buffer = new char[1024];while (true) {int n = reader.read(buffer);if (n == -1) {break;}//将buffer0下标的数据,读取n个.并以String类型返回String s = new String(buffer,0,n);//放入容器,将字符串拼接起来stringBuilder.append(s);}} catch (IOException e) {e.printStackTrace();}//判断容器中是否有key值if (stringBuilder.indexOf(key) == -1) {//文件中没有要查询的值return;}//到这里肯定就是有key值了System.out.println("找到了匹配的文件 " +  f.getAbsolutePath());}
}

我新建了个文件夹,里面存放了一些文本文件,内容如下:

结果:

相关文章:

JAVA如何操作文件?(超级详细)

目录 一、认识文件和相关知识 1.认识文件 2.⽬录 3.⽂件路径&#xff08;Path&#xff09; 4.文本文件和二进制文件的区分 二、File类操作文件 1.构造方法 2.方法 2.1 方法表 2.2 get相关的方法和构造方法 2.2.1 “.” 和 “..” 2.3 is相关的方法 2.4 删除相关…...

(2)VTK C++开发示例 --- 绘制多面锥体

文章目录 1. 概述2. CMake链接VTK3. main.cpp文件4. 演示效果 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;VTK开发 &#x1f448; 1. 概述 VTK C开发示例程序&#xff1b; 使用C 和VTK绘制一个多面锥体。 环境说明系统ubuntu22.04、windows11cmake3.22、3.2…...

(2025-04-12)向老主机箱中安装新买的显卡及固态硬盘

目录 1 引言2 显卡及其驱动的安装3 固态硬盘的安装及C盘扩容3.1 固态硬盘正确连接到主板上后&#xff0c;操作系统上面仍然不显示对应盘符怎么办&#xff1f;3.2 如何对C盘扩容&#xff1f;3.3 新问题&#xff1a;原有D盘程序不能运行 4 总结 1 引言 今天安装昨天买的新固态硬…...

rk3588 驱动开发(一)字符设备开发

3.字符设备驱动开发 3.1 什么是字符设备驱动 字符设备&#xff1a;就是一个个字节&#xff0c;按照字节流进行读写操作的设备&#xff0c;读写是按照先后顺序的。 举例子&#xff1a;IIC 按键 LED SPI LCD 等 Linux 应用程序调用驱动程序流程&#xff1a; Linux中驱动加载成功…...

AbMole| 体内相互作用筛选揭示肝脏对癌症转移的限制

癌症转移&#xff0c;作为导致患者死亡的主要原因之一&#xff0c;其复杂机制一直是医学界研究的热点。肝脏&#xff0c;作为癌症转移的常见靶器官&#xff0c;其微环境对癌症细胞的生长和转移具有重要影响。然而&#xff0c;肝脏如何限制癌症转移的具体机制尚不完全清楚。 来…...

STM32移植文件系统FATFS——片外SPI FLASH

一、电路连接 主控芯片选型为&#xff1a;STM32F407ZGT6&#xff0c;SPI FLASH选型为&#xff1a;W25Q256JV。 采用了两片32MB的片外SPI FLASH&#xff0c;电路如图所示。 SPI FLASH与主控芯片的连接方式如表所示。 STM32F407GT6W25Q256JVPB3SPI1_SCKPB4SPI1_MISOPB5SPI1_MOSI…...

2025年第十六届蓝桥杯省赛JavaB组真题回顾

第16届蓝桥杯省赛已经结束了&#xff0c;第一次参加也是坐牢了4个小时&#xff0c;现在还是来总结一下吧&#xff08;先声明以下的解法&#xff0c;大家可以当作一种思路来看&#xff0c;解法不一定是正解&#xff0c;只是给大家提供一种能够正常想到的思路吧&#xff09; 试题…...

Android 接口定义语言 (AIDL)

目录 1. 本地进程调用(同一进程内)2. 远程进程调用(跨进程)3 `oneway` 关键字用于修改远程调用的行为Android 接口定义语言 (AIDL) 与其他 IDL 类似: 你可以利用它定义客户端与服务均认可的编程接口,以便二者使用进程间通信 (IPC) 进行相互通信。 在 Android 上,一个进…...

c# 数据结构 链表篇 有关双向链表的一切

本人能力有限,如有不足还请斧正 目录 0.双向链表的好处 1.双向链表的分类 2.不带头节点的标准双向链表 节点类:有头有尾 链表类:也可以有头有尾 也可以只有头 增 头插 尾插 删 查 改 遍历 全部代码 3.循环双向链表 节点类 链表类 增 头插 尾插 删 查 遍历…...

Debian服务器挂载外部存储设备的完整指南

在 Debian 系统中挂载外部存储设备(如 U 盘、移动硬盘、SSD)是服务器运维和桌面使用中非常常见的操作。本文将为你详细拆解从识别设备、格式化到手动/自动挂载的全过程&#xff0c;适合新手到进阶用户参考。 一、准备阶段&#xff1a;插入存储设备并识别 1. 插入外部设备后查看…...

660 中值定理

文章目录 前言168169170总结 前言 background music: 《代替》 张叶蕾 660 上面没有专门的中值定理章节&#xff0c;我蒙了。不过应该可以找一下。就是证明题&#xff0c;标志性应该还行。找一下。然后做一下。660 的题质量应该还是非常高的。但是积分中值定理&#xff0c;还有…...

Elasticsearch:AI 助理 - 从通才到专才

作者&#xff1a;来自 Elastic Thorben Jndling 在 AI 世界中&#xff0c;关于构建针对特定领域定制的大型语言模型&#xff08;large language models - LLM&#xff09;的话题备受关注 —— 不论是为了更好的安全性、上下文理解、专业能力&#xff0c;还是更高的准确率。这个…...

数据结构——布隆过滤器

目录 一、什么是布隆过滤器&#xff1f; 二、布隆过滤器的原理 三、布隆过滤器的特点 一、什么是布隆过滤器&#xff1f; 布隆过滤器是一种空间效率高、适合快速检索的数据结构&#xff0c;用于判断一个元素是否可能存在于一个集合中。它通过使用多个哈希函数和一个位数组来…...

时间的重构:科技如何重塑人类的时间感知与存在方式

时间是人类认知的基石&#xff0c;也是科技发展的终极命题。从石英钟到量子计时器&#xff0c;从日晷到区块链时间戳&#xff0c;技术不断重构着我们对时间的理解与利用。然而&#xff0c;当人工智能、量子计算和脑机接口等前沿技术开始挑战时间的线性本质时&#xff0c;我们不…...

悄悄话识别、 打电话识别、攀高识别三种识别算法

在摄像头正对场景下,悄悄话识别(唇语识别)、打电话识别和攀高识别是三种典型的行为检测技术。以下从技术原理、算法模型、应用场景及挑战等方面进行详细分析: 一、悄悄话识别(唇语识别) 技术原理 唇语识别通过分析嘴唇的几何特征(形状、开合程度、运动轨迹)和动态变化…...

docker多架构镜像构建

docker多架构镜像构建 Docker 多架构镜像构建&#xff08;Multi-Architecture Image Build&#xff09;允许你为不同平台&#xff08;如 linux/amd64, linux/arm64, linux/arm/v7 等&#xff09;构建和推送统一的镜像标签&#xff0c;解决在不同硬件架构之间部署的问题。 Doc…...

机器学习常用算法总结

1. 概述 机器学习的定义是对于某类任务T和性能度量P&#xff0c;如果一个计算机程序在T上其性能P随着经验E而自我完善&#xff0c;那么我们就称这个系统从经验E中学习&#xff0c;机器学习是人工智能的一种方法&#xff0c;它通过在大量数据中学习隐藏的规则&#xff0c;模式和…...

软件架构设计:MVC、MVP、MVVM、RIA 四大风格优劣剖析

MVC、MVP、MVVM 和 RIA 都是软件架构中常见的设计风格&#xff0c;以下是对它们的详细介绍&#xff1a; 一、MVC 架构风格&#xff08;Model - View - Controller&#xff09; 1.简介&#xff1a;MVC 架构风格将软件应用程序分为三个核心部分&#xff0c;通过这种划分来分离不…...

Android12 自定义系统服务

在Android中可以通过两种方式创建系统服务: 由SystemServer启动的系统服务,使用SystemServer线程资源,适合轻量级的服务,比如各种XMS服务;占用独立进程,在系统启动时,由init进程拉起,比如SurfaceFlinger;本文采用的是第一种方式。 自定义AssistantManagerService 参…...

Flink SQL SavePoint最佳实践

以下是 Flink SQL Savepoint 最佳实践&#xff0c;涵盖配置、触发、恢复及注意事项&#xff0c;高效管理作业状态&#xff1a; 一、Savepoint 的配置与触发 1. 基础配置 存储路径&#xff1a;在 flink-conf.yaml 中全局设置 Savepoint 存储目录&#xff0c;避免每次手动指定路…...

【STM32】在FreeRTOS下使用硬件SPI收发数据出现的时序耦合问题(WK2124芯片为例)

问题 STM32中在Freertos使用SPI通讯芯片 WK2124进行SPI转4串口时&#xff0c;接收数据为一个任务&#xff0c;发送数据为一个任务&#xff0c;切接受任务优先级更高实测发现收发一段时间&#xff08;约几分钟&#xff09;外扩芯片会死锁导致WK2124复位。 分析 首先&#xff…...

关于香橙派OrangePi 5 Ultra 这个开源板子,开发Android

我下载了它资料中的开源Android13 系统SDK&#xff0c; 这个SDK连个git 都没有&#xff0c;把这种代码释放能称为开源吗&#xff1f;&#xff1f; 并且也就是说你买了这个板子&#xff0c;里面是没有任何关于RK3588的开发文档&#xff0c;如果你没玩过其他RK平台&#xff0c;估…...

ubuntu启动 Google Chrome 时默认使用中文界面,设置一个永久的启动方式

方法 &#xff1a;通过桌面快捷方式设置 编辑 Chrome 的桌面快捷方式&#xff1a; 找到您的 Google Chrome 快捷方式文件。如果是通过菜单启动&#xff0c;通常会在以下路径找到与 Chrome 相关的 .desktop 文件&#xff1a; sudo vim /usr/share/applications/google-chrome.d…...

字节跳动开源 Godel-Rescheduler:适用于云原生系统的全局最优重调度框架

背景 在云原生调度中&#xff0c;一次调度往往无法解决所有问题&#xff0c;需要配合重调度来优化资源分配和任务摆放。传统的重调度框架主要集中在识别异常节点或任务&#xff0c;并通过迁移或删除来解决。然而&#xff0c;这些框架往往只能解决局部问题&#xff0c;无法提供…...

WINUI——Background小结

在 ​​WinUI/UWP XAML​​ 中&#xff0c;Background&#xff08;或其他颜色属性&#xff09;支持 ​​多种颜色表示方式​​&#xff0c;包括以下三种主流格式&#xff1a; ​​1. RGB 十六进制&#xff08;不透明&#xff09;​​ ​​格式​​&#xff1a;#RRGGBB​​特点…...

Oracle数据库数据编程SQL<01. 课外关注:数据库查重方法全面详解>

查重是数据库管理和数据分析中的常见需求&#xff0c;以下是各种查重方法的全面总结&#xff0c;涵盖不同场景和技术手段。 更多Oracle学习内容请查看&#xff1a;Oracle保姆级超详细系列教程_Tyler先森的博客-CSDN博客 目录 一、基础SQL查重方法 1. 使用GROUP BY和HAVING …...

开源技术如何助力中小企业实现财务管理自主化?

中小企业的数字化困境与开源机遇 国际数据公司&#xff08;IDC&#xff09;研究显示&#xff0c;全球67%的中小企业因高昂的软件成本和僵化的功能设计&#xff0c;未能有效推进数字化转型。传统商业软件常面临三大矛盾&#xff1a; 功能冗余与核心需求缺失&#xff1a;标准化系…...

边缘计算与隐私计算的融合:构建数据经济的“隐形护盾“

在数据成为核心生产要素的今天&#xff0c;边缘计算与隐私计算的交汇正在重塑技术生态。这并非简单的技术叠加&#xff0c;而是一场关于数据主权、算力分配与信任机制的深度博弈。本文将从"数据流动的拓扑学"视角&#xff0c;探讨二者融合如何重构数字社会的基础设施…...

【大模型实战篇】--阿里云百炼搭建MCP Agent

MCP协议&#xff08;Model Communication Protocol&#xff0c;模型通信协议&#xff09;是大语言模型&#xff08;LLM&#xff09;与外部系统或其他模型交互时的一种标准化通信框架&#xff0c;旨在提升交互效率、安全性和可扩展性。 目录 1.阿里云百炼--MCP 1.1.MCP 服务接…...

基于PySide6与pycatia的CATIA智能倒角工具开发全解析

引言&#xff1a;工业设计中的倒角革命 在机械设计领域&#xff0c;倒角操作是零件加工前的必要工序。传统手动操作效率低下且易出错本文基于PySide6pycatia技术栈&#xff0c;提出一种支持批量智能倒角、参数动态校验、跨层级操作的自动化方案&#xff0c;其核心突破体现在&a…...