JavaEE-文件IO2

文章目录

  • 前言
  • 一、字节流
    • 1.1 读文件
    • 1.2 写文件
  • 二、字符流
    • 2.1 读文件
    • 2.2 写文件
  • 三、文件IO三道例题


前言

在这里对Java标准库中对文件内容的操作进行总结,总体上分为两部分,字节流和字符流,就是以字节为单位读取文件和以字符为单位读取文件内容。


一、字节流

1.1 读文件

字节流在Java中的类就是InputStream,他是一个抽象类,我们在这里操作的文件所以就需要通过它的子类FileInputStream向上转型的方式,因为InputStream不能初始化。后面如果我们要进行网络IO,就也是使用相对应的子类来进行实现。
代码示例1:

package io;import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;public class Demo6 {public static void main(String[] args) throws IOException {
//         读文件的两种不同参数的read方法的使用
//        InputStream inputStream = new FileInputStream("F:/test.txt");
//        while (true) {
//            int b = inputStream.read();
//            if (b == -1) {
//                break;
//            }
//            System.out.printf("0x%x ", b);
//        }
//        System.out.println();//        while (true) {
//            byte[] arrB = new byte[1024];
//            int n = inputStream.read(arrB);
//            System.out.println("n = " + n);
//            if(n==-1) {
//                //读毕 n就会返回-1
//                // 在这里首先第一次会把数组填满 后面第二次循环文件内没字节了就返回-1
//                break;
//            }
//            for (int i = 0; i < n; i++) {
//                System.out.printf("0x%x ", arrB[i]);
//
//            }
//            System.out.println();
//        }
//
//    }      }}

这段代码是使用字节流来读取文件内容的一个简单过程,因为我们读取硬盘中的文件内容给内存中的变量去接受,显然数据是往cpu的方向流动的,因此是一个输入的过程,我们使用InputStream抽象类以及FileInputStream类进行向上转型来构造这样的一个字节流对象。我们想要读取哪个文件中的信息就可以将文件的路径或者File类对象用以初始化字节流对象,如下。

InputStream inputStream = new FileInputStream("F:/test.txt");

FileInputStream这样的字节流对象提供了一些读取文件数据的方法如下图。
在这里插入图片描述
这三种方法有些许的不同,首先read方法,它每次读取一个字节,然后返回值就是就是这个字节的相应的ASCII值,如果说读到文件末尾那么此时就会返回-1。那么既然每次都是读一个字节,返回的就是一个字节的范围,那么返回值类型直接设为byte即可,那么为什么会设为int类型的返回呢?
有以下几点原因:
(1)确保每次返回的数都是正数,因为从原则上说字节这样的概念本身是无符号的但是byte类型本身是有符号的,如果说你拿byte来返回无符号数,那么范围是0~255,此时就无法表示-1。只有你使用int类型,才能够返回值是正数,还能使用-1来表示文件结尾。
(2)这时我们又会想到,那么我们直接用short不就行了,这样也就可以包含-1以及0到255的整数。但是这里就涉及到计算机发展的问题了,因为计算机发展到现在存储空间不再是核心矛盾了,存储设备的成本是越来越便宜了,此时随着cpu越来越牛,它单次处理数据的长度也越来越长。对于32位cpu,一次就能够处理四个字节的数据,此时要是使用short还要将其转成int再按int进行处理,显然64位cpu也是类似的,此时的short就更没意义了,我们学过的c语言中的整形提升也是类似的道理。因此在我们使用short的场景换成int,在我们使用float的场景我们换成double。
这里补充一下,为什么说字节数据从原则上是无符号的,因为字节数据不是用来进行算数运算的,例如一张图片就是由很多字节数据构成的,如果对其字节进行加一或者减一操作,那么这张图像很可能直接崩掉
第二个read方法和第一个read方法不同的点在于它的参数是一个字节数组,这个数组是一个空数组,读文件数据的时候就会把读到的数据全放到这个数组当中,去把这个字节数组给填满,能填多少填多少。返回值也是类似,你读到多少个字节就返回多少,读到文件尾就返回-1。
第三个read方法其实和第二个read方法就很类似了,也是建立一个空数组来作为参数,然后指定一个区间,读到的文件数据只放到这个空数组的对应区间当中。返回值就和第二个read方法一样了,读到多少字节就返回多少,读到文件尾就返回-1。
这里提一嘴,read()和read(byte[] b)这两种方法谁的效率比较高?
事实上是第二个方法的效率高,我们都知道第一个方法是一次读取一个字节,第二个方法是一次读一个数组的字节,对于固定的文件内容,肯定是第二个方法读取的次数比较少,文件的IO在我们的代码中是一个比较低效的操作,每次读取都要进行一次IO,显然IO次数少的方法效率就更高。
此时我们把视角转回上面的代码示例1,我们分别使用方法1和方法2来读取文件数据,然后在外面套一个死循环,当read方法返回-1代表读到文件尾此时跳出循环,逻辑还是比较简单的。
代码示例2:

package io;import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;public class Demo6 {public static void main(String[] args) throws IOException {// 但是每当建立一个线程或进程 建立的PCB中的文件描述符表这个顺序表它的长度是有限的// 每当打开一个文件 它的长度就要加一、// 所以打开文件后要及时关闭// 所以这里联想到处理unlock()的方法 即使用try finally语句
//        InputStream inputStream = new FileInputStream("F:/test.txt");
//        try {
//            while (true) {
//                byte[] arrB = new byte[1024];
//                int n = inputStream.read(arrB);
//                System.out.println("n = " + n);
//                if (n == -1) {
//                    //读毕 n就会返回-1
//                    // 在这里首先第一次会把数组填满 后面第二次循环文件内没字节了就返回-1
//                    break;
//                }
//                for (int i = 0; i < n; i++) {
//                    System.out.printf("0x%x ", arrB[i]);
//                }
//                System.out.println();
//            }
//        } finally {
//            inputStream.close();
//        }}}}

上述代码相对于代码示例1加入了try-finally这样的代码,因为如同代码中注释所描述的每次你打开一个文件,就会在PCB当中的文件描述符表当中添加一个元素,这个元素当中就是文件的相关信息,文件描述符表类似于一个顺序表,它总会有上限当这样不关闭文件的操作多了,会占满文件描述符表,此时若是再想打开文件就不行了。因此每次我们使用FileInputStream构造流对象打开文件读取文件内容等一系列操作之后就要关闭文件,使用try-finally就可以保证每次使用完文件之后能够关闭文件的流对象,此时文件描述符表中的对应元素就会被释放。
代码示例3:

package io;import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;public class Demo6 {public static void main(String[] args) throws IOException {// 使用try finally确实可以 但是作为一个程序员要追求去写优雅的代码// 上述代码修改如下// 直接将流对象的创建放到try右边的括号中 这样java会自动帮你调用close()// 注意不是什么对象都可以这样 必须要实现Closeable接口的类才可以try (InputStream inputStream = new FileInputStream("F:/test.txt");){while (true) {byte[] arrB = new byte[1024];int n = inputStream.read(arrB);System.out.println("n = " + n);if (n == -1) {//读毕 n就会返回-1// 在这里首先第一次会把数组填满 后面第二次循环文件内没字节了就返回-1break;}for (int i = 0; i < n; i++) {System.out.printf("0x%x ", arrB[i]);}System.out.println();}}}}

代码示例2我们解决了关闭文件流对象释放文件描述符表中元素的问题,但是使用try-catch好像不够优雅,于是我们将InputStream流对象构造的这条语句放入try,这样java会在文件操作完成之后自动给我们的流对象调用close方法,但是要注意的一点就是这种编写方式的前提是放入try后的括号的对象对应的类必须实现了Closeable接口。
另外在我们使用FileInputStream类对象时可以配合Scanner对象来进行使用,代码示例如下:

package io;import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;public class  Demo10 {public static void main(String[] args) {try (InputStream inputStream = new FileInputStream("F:/test.txt")) {Scanner sc = new Scanner(inputStream);// 本来这里要在控制台输入 但是现在直接将文件中的数据读走了sc.next();} catch (IOException e) {throw new RuntimeException(e);}}}

以前我们使Scanner对象都是在终端输入数据,这样写代码就是将我们从终端输入的数据换成了文件中的数据。另外能这么写的原因看下图Scanner类的构造函数的参数就不难理解了,本来就是InputStream类型的,另外我们在之前写入Scanner对象的System.in也是InputStream类型的。
在这里插入图片描述

1.2 写文件

写文件的流程和读文件的流程类似的,需要使用OutputStream抽象类以及其子类FileOutputStream。写文件也要通过FileOutputStream对象的write方法来实现,FileOutputStream对象的write也是要分为三个版本,如下图。
在这里插入图片描述
和前面的read参数很类似,第一个版本就是一次在文件中写入一个字节,写入的字节由b指定。第二个版本就是一次写入文件一整个数组,第三个版本也是往文件中写入一个数组,只是只写入数组在区间内的部分。
代码示例如下:

package io;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;public class Demo7 {public static void main(String[] args) {try (OutputStream outputStream = new FileOutputStream("F:/test.txt",true)) {// 注意这里打开文件会发现文件内原本的内容被清空 里面全是新添加的内容// 这里的清空操作是打开文件时清空的,即构造对象时清空的// 要是想在文件内追加即append内容 只需在构造对象时将第二个参数设为trueoutputStream.write(97);outputStream.write(98);outputStream.write(98);outputStream.write(99);} catch (IOException e) {throw new RuntimeException(e);}}}

这里写入文件的代码需要注意一点就是构造FileOutputStream对象时会直接将对应路径上的文件内容给清空,如果想要实现写入文件是一种append的效果,就要在构造流对象时将第二个参数设为true。示例代码中的注释也说明了。

二、字符流

使用字符流和字节流操作文件内容基本的流程是类似的,但是字符流读取和写入文件内容的基本单位是字符,字节流是字节。

2.1 读文件

使用字符流读文件过程和使用字节流读文件的流程是相似的,只有很少的差别。
代码示例如下:

package io;import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;public class Demo8 {public static void main(String[] args) {try (Reader reader = new FileReader("./testDir/test2.txt")) {// 读入字符的时候要思考一个问题// 文件中的字符编码集用的是utf8 一个中文字是三个字节// java中的一个中文字是两个字节// 为什么java中的char还能接收并且打印中文字// 原因:因为在java中的char类型在接收字符时会自动将utf8转换为unicode编码集// 实际上java中很多类型在接收数据时都会进行字符集的转换while (true) {int n = reader.read();if(n==-1) {break;}System.out.printf("%c ",n);}} catch (IOException e) {throw new RuntimeException(e);}}}

如以上的示例代码,与字节流的FileinputStream不同这里读文件内容使用的是FileReader类,过程还是类似的,外面套个循环,每次读取一个字符,直到读取操作返回值为-1代表文件读完此时跳出循环。
但是这里会有一个疑问,在windows下文件中的字符是采用utf8的编码集,在这个编码集当中单个汉字的字节数是三个,为什么在java中还能使用char类型接收汉字并且打印,char类型在java中只有两个字节。这个问题的答案就是说java中读取到文件中内容时会自动转换编码集,对于char类型java中使用的时unicode编码集,读到的文件中汉字会自动转为char类型,也就会经过utf8到unicode这个过程,在unicode当中汉字占两个字节。
在java中不同的类型实际上用的编码集都是不同的,比如说String类型内部是使用utf8,char类型使用的是unicode。使用String类型变量保存你好就需要6个字节,当使用字符串s.charAt()这种方法将字符赋给char类型变量时,就会将编码集从utf8转为unicode,此时一个字就从三个字节转为了两个字节。

2.2 写文件

流程都是类似的,写文件的操作主要是通过Writer类中的write方法来实现的。
代码示例如下:

package io;import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;public class Demo9 {public static void main(String[] args) {try (Writer writer = new FileWriter("F:/test.txt")) {// 字符流写文件和字节流一样 都是要加上一个true这个参数才能实现append这样的效果writer.write("你好世界");} catch (IOException e) {throw new RuntimeException(e);}}
}

和字节流的write一样,要想在写文件时实现append的效果就需要在构建FileWriter对象时将第二个参数设为true,否则写文件时都会先清空路径上的文件内容。

三、文件IO三道例题

(1)扫描指定目录,并找到名称中包含指定字符的所有普通文件(包含目录)。

package io;import java.io.File;
import java.util.Scanner;public class Demo11 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);System.out.println("输入要查找文件的目录:");String dirPath = sc.nextLine();System.out.println("输入要查找文件的关键词:");String keyWord = sc.nextLine();// 根据输入的路径构造file对象File dir = new File(dirPath);// 如果输入的路径不是目录则直接返回并报错if (!dir.isDirectory()) {System.out.println("输入路径非法!");return;}// 关键词和目录都是正确的那么就开始查找文件searchFile(dir, keyWord);}private static void searchFile(File dir, String keyWord) {// 将目录下的所有文件输出成数组File[] files = dir.listFiles();// 如果数组为空直接返回 这也是递归结束的条件if (files == null) {return;}// 遍历数组for (File file : files) {// 如果文件是文件 那么判断是否包含关键词if (file.isFile()) {// 包含则匹配成功if (file.getName().contains(keyWord)) {System.out.println("匹配成功:" + file.getAbsoluteFile());}// 如果是目录则进行递归 在下一级目录进行查找} else if (file.isDirectory()) {searchFile(file, keyWord);}}}
}

(2)复制文件,输入一个路径,表示要被复制的文件,输入另一个路径,表示要复制到的目标路径。

package io;import java.io.*;
import java.util.Scanner;public class Demo12 {public static void main(String[] args) throws IOException {Scanner sc = new Scanner(System.in);System.out.println("输入要复制的文件路径:");String srcPath = sc.nextLine();System.out.println("输入要复制到的文件路径");String destPath = sc.nextLine();File fileSrc = new File(srcPath);File fileDest = new File(destPath);if (!fileSrc.isFile()) {System.out.println("输入的要复制的文件路径不合法!");return;}if (!fileDest.getParentFile().isDirectory()) {System.out.println("输入的复制到的文件路径不合法!");return;}byte[] bytes = new byte[1024];// OutPutStream会自动创建文件try (InputStream inputStream = new FileInputStream(fileSrc);OutputStream outputStream = new FileOutputStream(fileDest)) {while (true) {int n = inputStream.read(bytes);if (n == -1) {break;}outputStream.write(bytes, 0, n);}} catch (IOException e) {throw new RuntimeException(e);}}
}

(3)输入一个路径再输入一个查询词,搜索这个路径中文件内容包含这个查询次的文件。

package io;import java.io.*;
import java.util.Scanner;public class Demo13 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);System.out.println("请输入目标目录:");String dirPath = sc.nextLine();System.out.println("请输入要匹配的关键词:");String keyWord = sc.nextLine();File dirFile = new File(dirPath);if (!dirFile.isDirectory()) {System.out.println("输入路径不合法!!");return;}search(dirFile, keyWord);}private static void search(File dirFile, String keyWord) {File[] files = dirFile.listFiles();if (files == null) {return;}for (File f :files) {if (f.isFile()) {match(f, keyWord);} else if (f.isDirectory()) {search(f, keyWord);}}}private static void match(File f, String keyWord) {StringBuilder stringBuilder = new StringBuilder();try (Reader reader = new FileReader(f)) {while (true) {int b = reader.read();if (b == -1) {break;}stringBuilder.append((char) b);}} catch (IOException e) {throw new RuntimeException(e);}if (stringBuilder.indexOf(keyWord) >= 0) {System.out.println("匹配成功:" + f.getAbsolutePath());}}}

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

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

相关文章

[AI Google] 介绍 VideoFX,以及 ImageFX 和 MusicFX 的新功能

VideoFX 是来自 labs.google 的最新实验&#xff0c;您可以查看音乐效果和图像效果的新更新&#xff0c;现在在 110 多个国家可用。 生成式媒体正在改变人们构思创意并增强我们的创造力能力的方式。我们致力于与创作者和艺术家合作构建人工智能&#xff0c;以更好地理解这些生成…

cmake使用交叉编译工具链并验证

目录 一、内容 二、配置 1. 准备cmake文件 2. 使用交叉编译 三、验证 1. 构建阶段验证 2. 编译阶段验证 一、内容 目的&#xff1a;在X86环境下编译ARM平台软件 编写交叉编译配置文件&#xff1a;xx.cmake 执行cmake命令时指定&#xff1a;cmake \ -DCMAKE_TOOLCHAIN_F…

K8S认证|CKA题库+答案| 12. 查看Pod日志

目录 12、查看Pod日志 CKA v1.29.0模拟系统免费下载试用&#xff1a; 题目&#xff1a; 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、提取错误日志 3&#xff09;、验证提取结果 12、查看Pod日志 CKA v1.29.0模拟系统免费下载试用&#xff1a; 百度…

简单的UDP网络程序:多人群聊系统

本章重点 能够实现一个简单的udp客户端/服务器; 1.创建套接字 我们把服务器封装成一个类&#xff0c;当我们定义出一个服务器对象后需要马上初始化服务器&#xff0c;而初始化服务器需要做的第一件事就是创建套接字。 ⭐参数说明&#xff1a; domain&#xff1a;创建套接字的域…

Nginx代理配置(专业版)

写在前面提醒&#xff1a;使用代理&#xff0c;如果可以&#xff0c;请尽量支持双协议&#xff0c;http、https均要支持哈。 注意&#xff1a;监控系统只是运行代码&#xff0c;是否支持https&#xff0c;需要运维同学在你们的服务器上配置https证书&#xff0c;配置好证书&…

在 CentOS 上安装 PostgreSQL 的全面指南

PostgreSQL 是一种功能强大的开源关系型数据库管理系统&#xff0c;广泛应用于各种领域。它提供了诸如事务处理、并发控制和数据完整性等高级功能&#xff0c;因此深受开发者和企业的欢迎。本指南将逐步引导您在 CentOS 上安装 PostgreSQL&#xff0c;以便您充分利用其众多优势…

决定了,将ChatGPTer开源!主打一个大模型人人可用。

一个快速上手且极易部署的类ChatGPT开源应用&#xff0c;可接入 OPENAI API 或 通义千问API 开源地址&#xff1a; https://github.com/isnl/EsChat 大声(偷偷)告诉你&#xff1a;通义千问有免费API额度可白嫖&#xff01;&#xff01;&#xff01; 版本特性 OPENAI 和 通义千…

利用英特尔 Gaudi 2 和至强 CPU 构建经济高效的企业级 RAG 应用

检索增强生成 (Retrieval Augmented Generation&#xff0c;RAG) 可将存储在外部数据库中的新鲜领域知识纳入大语言模型以增强其文本生成能力。其提供了一种将公司数据与训练期间语言模型学到的知识分开的方式&#xff0c;有助于我们在性能、准确性及安全隐私之间进行有效折衷。…

任推邦:实力强劲的APP推广拉新平台,号称不扣量

任推邦简介 任推邦是国内数一数二的项目分发平台&#xff0c;也是一个不扣量的项目APP推广拉新平台&#xff0c;隶属于聚名科技集团股份有限公司。聚名科技成立时间在2012年&#xff0c;是安徽省老牌互联网企业&#xff0c;历经11年的飞速发展&#xff0c;聚名科技成功布局打造…

小程序的这些知识你知道吗?

一:导航传参 无论是编程式还是声明式导肮传参都是在url?keyvalue&key1value1,无论是否是tabbar页面. 对于回退页面,没办法传参. 这个参数是,跳转到页面的时候,跳转到另一个页面,这个页面就是刚开始执行,等数据执行之后,触发onload,传递的参数放在内存中,跳转是内部底层触…

云端力量:利用移动云服务器高效部署Spring Boot Web应用

文章目录 一、移动云介绍二、移动云产品选择三、体验云主机ECS四、使用移动云服务器部署SpringBoot Web应用4.1移动云ECS安装JDK4.2移动云ECS安装MySQL4.3移动云ECS数据库插入数据4.4移动云ECS部署Spring Boot Web应用 总结 一、移动云介绍 移动云是中国移动基于自研的先进技术…

Linux中常见的基本指令(上)

目录 一、ls指令 1. ls 2. ls -l 3. ls -a 4.ls -F 二、qwd指令 三、cd指令 1. cd .. 2. cd / / / 3. cd ../ / / 4. cd ~ 5. cd - 五、mkdir指令 六、rmdir指令和rm指令 一、ls指令 语法 &#xff1a; ls [ 选项 ][ 目录或文件 ] 。 功能 &#xff1a;对于目录…

桶排序和基数排序

前言&#xff1a; 这篇文章&#xff0c;我们就来了解一些鲜为人知的排序&#xff0c;桶排序和基数排序。 桶排序&#xff1a; 桶排序的思想&#xff1a; 桶排序的思想就是把待排序的数尽量均匀地放到各个桶中&#xff0c;再对各个桶进行局部的排序&#xff0c;最后再按序将各…

AI Agent: Agent框架+7个实例

何谓Agent Agent 作为一种新兴的人工智能技术&#xff0c;正在受到越来越多的关注。要说清楚什么是 Agent&#xff0c;先得看看人工智能的本质是什么。 人工智能这个名称来自它试图通过计算机程序或机器来模拟、扩展和增强人类智能的 一些方面。在这个定义中&#xff0c;“人…

C# WPF入门学习(四)—— 按钮控件

上期介绍了WPF的实现架构和原理&#xff0c;之后我们开始来使用WPF来学习各种控件。 一、尝试插入一个按钮&#xff08;方法一&#xff09; 1. VS2019 在界面中&#xff0c;点击工具栏中的视图&#xff0c;在下拉菜单中选择工具箱。 至于编译器中的视图怎么舒服怎么来布置&am…

服务器硬件全攻略:从入门到精通,全面解析服务器性能与稳定性!

服务器是计算机网络中提供特定服务的计算机系统&#xff0c;其硬件配置和性能直接影响到整个网络系统的运行效率和稳定性。作为一个资深的技术人员&#xff0c;本文将全面详细地介绍服务器硬件基础知识&#xff0c;包括介绍、命令或语法、主要作用以及使用方法等。 一、介绍 服…

Linux基础(七):Linux 系统上的库文件生成与使用

学过C语言我们知道&#xff0c;C语言有标准库和自定义库&#xff0c;这些方便了我们的实际开发&#xff0c;提供了已经实现好的函数接口&#xff0c;我们使用的时候&#xff0c;只需要引入头文件即可&#xff0c;那具体的实现过程又是怎么样的呢&#xff1f;我们又该如何实现我…

Mysql中的慢查询

Mysql慢查询的一些sql命令 慢查询的默认事件为10秒 #注意&#xff1a;慢查询一般是在调试阶段开启的&#xff0c;在开发阶段中一般不会开启&#xff0c;会对效率产生延误 #查询慢查询是否开启 show variables like %general%; #慢查询时间设置 show variables like long_query…

查找专利渠道

官方渠道 常规检索 (cnipa.gov.cn)https://pss-system.cponline.cnipa.gov.cn/conventionalSearch 佰腾网 佰腾网 - 查专利就上佰腾网_佰腾全球专利搜索平台_商标查询平台_企业工商信息查询平台 (baiten.cn)https://www.baiten.cn/

NLP(19)--大模型发展(3)

前言 仅记录学习过程&#xff0c;有问题欢迎讨论 大模型训练相关知识&#xff1a; 问题&#xff1a; 数据集过大&#xff0c;快速训练模型过大&#xff0c;gpu跑不完 方案&#xff1a; 数据并行训练&#xff1a; 复制数据&#xff08;batch_size&#xff09;到多个gpu&…