用 Java 的 IO 流进行读写文件操作

news2024/9/23 13:21:08

前言

在计算机领域里 IO,有时也写作 I/O,是Input / Output的缩写,也就是输入和输出。这里的输入和输出是指不同系统之间的数据输入和输出,比如读写文件数据,读写网络数据等等。

本文内容大纲如下:

Java 有哪些IO框架

Java 中有三代 IO 框架,分别是第一代的同步阻塞 IO (也叫 BIO, Blocking IO),第二代的NIO ,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层的高性能数据操作方式。第三代 NIO2 有的地方也叫 AIO,即Async IO,进一步支持了异步IO。

这些 IO 框架都是针对文件的,网络通信同样属于 IO 行为,但是被 Java 单独放在了 java.net 包下,不在这里说的 IO 体系内。

这个教程中我们来学习 Java IO 体系中最简单和易于理解的同步阻塞 IO,后面有了这里的知识积累后再去进一步学习 NIO 和 AIO。

BIO 简介

同步阻塞 IO 即 BIO(blocking IO),指的主要是传统的 java.io 包,它基于流模型实现。java.io 包提供了我们最熟知的一些 IO 功能,比如 File 对象提供的文件和目录操作,还有一大块就是通过输入输出流读写文件等。

BIO 交互方式是同步、阻塞的方式,也就是说,在读取输入流或者写入输出流时,在完成之前,线程会一直阻塞在那里。多个 IO 调用的执行顺序是线性顺序。不过 BIO 的优点是代码比较简单、直观,虽然不适合在高并发场景下使用,但足够应对普通场景,同时也更容易学习和掌握。

IO 流

IO 流是 Java IO 中的核心概念。流是在概念上表示无穷无尽的数据流。IO 流连接到数据源或数据的目的地,连接到数据源的叫输入流,连接到数据目的地的叫输出流。 Java 程序不能直接从数据源读取和向数据源写入,只能借助 IO 流从输入流中读取数据,向输出流中写入数据。

Java IO 中的流可以是基于字节的(读取和写入字节)也可以基于字符的(读取和写入字符),所以分为字节流和字符流,两类流根据流的方向都可以再细分出输入流和输出流。

  • 字节流
    • 输入字节流:InputStream
    • 输出字节流:OutputStream
  • 字符流
    • 输入字符流:Reader
    • 输出字符流:Writer

这里有一点可能容易让人迷惑的是,IO中的输入和输出指的是相对于程序的输入和输出,程序向外输出内容,会向输出流里写入,虽然写入操作看似是输入,但相对于程序本身而言它是在向外输出内容。所以程序写的是OutputStream 读的是InputStream

字节流

字节流主要操作字节数据或二进制对象。 字节流有两个核心抽象类:InputStream 和 OutputStream。所有的字节流类都继承自这两个抽象类。 ##

字符流

字符流操作的是字符,字符流有两个核心类:Reader 类和 Writer 。所有的字符流类都继承自这两个抽象类。

字节流、字符流怎么选择

字节流和字符流都有 read()write()flush()close() 这样的方法,这决定了它们的操作方式近似。

  • 字节流的数据是字节(二进制对象)。主要核心类是 InputStream 类和 OutputStream 类。
  • 字符流的数据是字符,主要核心类是 Reader 类和 Writer 类。

所有的文件在硬盘或传输时都是以字节方式保存的,例如图片,影音文件等都是按字节方式存储的。字符流无法读写这些文件。

所以,除了纯文本数据文件使用字符流以外,其他文件类型都应该使用字节流方式。

字节流到字符流的转换可以使用 InputStreamReader 和 OutputStreamWriter。使用 InputStreamReader 可以将输入字节流转化为输入字符流,使用OutputStreamWriter可以将输出字节流转化为输出字符流。

下面我们通过一个读写文本文件的程序来演示一下字节流到字符流的转换以及 Java IO 的基本操作。

实践–Java IO 读写文本文件

Java IO 中的类非常多,对应的方法也很多,一一罗列会导致内容过于枯燥,所以我们写两个用 IO 流写文件和读文件的例子,来展示下怎么使用 IO 流读写文件。

下面主要使用的是 FileOutputStream / FileInputStreamIO 流绑定到 File 对象上,然后将这两个字节流通过OutputStreamReader / InputStreamReader 转换为字符流,并设置字符编码,最后再用 PrintWriter / BufferedReader 给字节流增加缓冲更能,让程序能更方便地以行为单位操作 IO 流。

理解和掌握了这两个基本的用法后,其他 IO 流的使用也就不是什么难事儿了。

写文件示例程序

我们先来个写文件的示例小程序,在这个程序里面除了用到了 Java 文件、字节输出流等相关的知识外,还会用到我们前面在 Java 异常通关指南里讲过的帮助我们自动回收已打开资源的 try-with-resource形式的异常处理,Java 交互式获取命令行输入的 Scanner工具等。算是对我们专栏以前知识的一个实践应用和复习。

如果你对这些知识还有点生疏或者忘记了,也不用先着急回看,在这个示例程序的注释里会把这些知识点进行相关提示,下面也有对程序每个重要部分的详细解释,我们先来看例子。

这个例子里我们运行程序后,Java 程序会在命令行界面等待用户的输入,先让用户从命令行界面输入想要保存内容的文件的名字,再让用户输入内容。内容支持多行输入,直到遇到空行,程序会认为输入完毕,然后 Java 用用户指定的名字在项目目录下创建一个文件,最后把程序读取到的所有内容输入,写到文件里去。

package com.learnfile;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class WriteToFilesAppMain {

    private static final Scanner in = new Scanner(System.in);

    public static void main(String[] args) throws IOException {
        File targetFile = createFile();

        writeToFile(targetFile);

        System.out.println("程序执行结束");

    }

    private static void writeToFile(File targetFile) throws IOException {
        // 使用 try with resource 自动回收打开的资源
        try (
                // 创建一个outputstream 建立一个从程序到文件的byte数据传输流
                FileOutputStream fos = new FileOutputStream(targetFile);
                // 创建一个可以使用outputstream的Writer,并制定字符集,这样程序就能一个一个字符地写入
                OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
                // 使用PrintWriter, 可以方便的写入一行字符
                PrintWriter pw = new PrintWriter(osw);
         ) {
            System.out.println("输入的内容会实时写入文件,如果输入空行则结束");
            while (true) {
                String lineToWrite = in.nextLine().trim();
                System.out.println("输入内容为:" + lineToWrite);
                if (lineToWrite.trim().isBlank()) {
                    System.out.println("输入结束");
                    break;
                } else {
                    pw.println(lineToWrite);
                    // 真正用的时候不要写一行就flush() 这里只是演示
                    pw.flush();
                }
            }
            // 平时用的时候放在外面 flush
            // pw.flush();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static File createFile() throws IOException {
        System.out.println("请输入文件名:");
        String fileName = in.nextLine().trim();
        File f = new File("." + File.separator + fileName +".txt");
        if (f.isFile()) {
            System.out.println("目标文件存在,删除:" + f.delete());
        }
        System.out.println(f.createNewFile());
        return f;
    }
}

复制代码

这个示例程序里我们需要重点关注以下几个方面的知识点

  • 上面例程里使用了Scanner,以命令行交互的方式让我们能输入程序将要创建文件的名称和要往文件里写入的内容。
  • Java 的 IO流在使用完成后需要统一调用close()方法把流关闭掉。IO流的关闭会让程序释放出它们占用的内存资源,而且字符流操作时使用了缓冲区,并在关闭字符流时会强制将缓冲区内容输出,如果不关闭流,则缓冲区的内容是无法输出的。
  • 如果想在不关闭流时,就将缓冲区的内容输出到文件,可以调用流的flush()方法强制清空流使用的缓冲区。
  • 上面的示例程序里我们使用了try-with-resource形式的异常处理,把资源的关闭交给了Java– 在资源被使用完成后或者程序出现异常终止执行时都会由 Java 自动关闭在try-with-resource中打开的流资源。
  • 把字符串内容写入到文件的程序中,我们首先使用了 FileOutputStream 把目标文件绑定到字节输出流,再用 OutputStreamWriter 创建一个可以使用OutputStreamWriter,并指定其字符集为UTF_8,这样程序就能一个字符一个字符地写入文件啦。
  • 接下来程序在OutputStreamWriter 字符流的基础上创建了 PrintWriter,使用 PrintWriter 可以让程序方便地写入字符串,并且也可以通过它的 println 方法来自动处理换行符。

用 Java 程序完成文件的写入操作后,我们再来看看,给定一个文件,怎么用 Java 程序读取器中的内容。

读文本示例程序

我们先在要执行程序的目录下,添加一个测试用的名为 file.txt 的文本文件,如果是用Intelij IDEA 这样的 IDE 工具执行程序的话,可以在项目根目录下添加这个文件。

创建好文件后,在文件里随便输入几行内容用于测试:

aaa
一二三
bbb
四五六
ccc
七八九
ddd
锟斤拷
烫烫屯屯
复制代码

接下来我们用 Java 程序读取这个文件里的内容,这次我们则是会用到 IO 输入流相关的几个类: FileInputStream, InputStreamReader, BufferedReader等;他们的具体功能作用我写在了程序的注释里

package com.learnfile;

import java.io.*;
import java.nio.charset.StandardCharsets;

public class ReadStringFromFileAppMain {
    private static final String SOURCE_FILE_NAME = "file1.txt";

    public static void main(String[] args) {

        File sourceFile = new File("." + File.separator + SOURCE_FILE_NAME);

        ReadLineFromFile(sourceFile);
    }
    private static void ReadLineFromFile(File sourceFile) {
        try (
            // 建立从文件到程序的数据输入(input)流
            FileInputStream fis = new FileInputStream(sourceFile);
            // 用 InputStreamReader 包装 byte流,并指定字符编码,让它能够将读出的byte转为字符
            InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
            //  增加缓冲功能,输入输出效率更高,并且可以一次读一行
            BufferedReader reader = new BufferedReader(isr)
        ) {
            String line = null;
            while ((line = reader.readLine()) != null) {
                System.out.println(line.trim().toUpperCase());
            }
            // 还可以从reader里获取 Stream,然后通过流操作+Lambda的形式完成
            // reader.lines().map(String::trim).map(String::toUpperCase).forEach(System.out::println);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
复制代码

上面这个示例程序会用 Java 的 IO 输入流从文件中每次读取一行,并把行内容应用 toUpperCase() 方法后输出到终端,程序里打开的流资源的方式仍然是交给try-with-resource机制自动处理,这里不再赘述。

和输出流一样,在读取文件内容的程序中我们也做了字节流到字符流的转换,先用 FileInputStream 把文件绑定到了字节流上,然后通过 InputStreamReader 把字节流转换为字符流,在这个过程中也是设置了字符编码,最后又用 BufferedReader 为字符流增加缓冲功能,这样读取的效率会更高一些,通过它程序可以一次读取文件的一整行。

通过 BufferedReaderreadLine()遍历或者 lines() 获取Stream 后进行流处理,都可以用 BufferedReader 完成文件内容的遍历读取,上面在程序代码和注释里我们演示了两种遍历 BufferedReader 的方法。

总结

这篇文章我们介绍了 Java IO流里面的各种流,比较难记,常用的怎么把文件转成文件流,文件流转成字节和字符流以及设计字符的编码都给大家介绍了,把文章中提供的两个例子看明白差不多就算入门了。

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

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

相关文章

数据结构【AVL树模拟实现】

目录 AVL树概念 AVL树结构 insert AVL树的旋转 新节点插入较高右子树的右侧---右右:左单旋 新节点插入较高左子树的左侧---左左:右单旋 新节点插入较高左子树的右侧---左右:先左单旋再右单旋 新节点插入较高右子树的左侧---右左&…

Bug解决:出现C++:internal compiler error: killed(program cc1plus)

最近在学习hyperscan过程中,安装的过程总是会出现 C:internal compiler error: killed(program cc1plus) 反复查找之后既不是版本问题也不是依赖问题,查阅了很多解决方案后,确认是交换空间不足,所以我们解决的方法是临…

Kotlin 开发Android app(七)上:Kotlin函数fun

对于函数来说,现在的语言越来越想把它往变量上靠。 确实对于函数来说,他应该有很多变量的特性。 在Kotlin 中,定义函数是很简单的一件事情,我觉得编程有的时候是被一些语言给高复杂了,命名很简单的一些事情&#xff0…

matlab绘图

clear a-2:0.1:2; b-3:0.1:3; [x,y]meshgrid(a,b); z(1-(x.2)/4-(y.2)/9).^(1/2); mesh(x,y,z) hold on mesh(x,y,-z) clear a-1:0.1:1; b-2:0.1:2; [x,y]meshgrid(a,b); z(4/9)*(x.2)(y.2); mesh(x,y,z)

运维监控系统 PIGOSS BSM 拓扑自动发现原理

PIGOSS BSM提供了自动发现拓扑功能,能够发现全网拓扑、指定网段拓扑、路由拓扑,能够自动关联系统已经监控的设备的状态在拓扑图上实时显示。在做全网发现的时候,可以指定网段或起始路由器,并设定全网的snmp配置参数后,…

vue 使用webpack打包,出现路径404 ,导致白屏webpack-bundle-analyzer使用

使用webpack打包时,遇到两个问题,导致页面出现白屏 一个是cdn对于静态文件限制大小,不能大于2MB,超过这个大小后,就不能上传cdn成功,导致页面加载时,长时间白屏,且找不到资源&#x…

INTERSPEECH 2022|FS-CANet: 基于全带子带交叉注意力机制的语音增强

INTERSPEECH 2022 FS-CANet: 基于全带子带交叉注意力机制的语音增强 本文由清华大学与腾讯天籁实验室、香港中文大学合作,提出了一个全带-子带交叉注意力(FSCA)模块来交互融合全局信息和局部信息,并将其应用于FullSubNet&#…

Dubbo源码(十) 与Spring一起学习Dubbo里的Aware

目录 一、Spring 1.BeanNameAware 2. BeanClassLoaderAware 3. ApplicationContextAware 4. EnvironmentAware 5. ApplicationEventPublisherAware 6. aware注入时机 二、Dubbo 1. ExtensionAccessorAware 三、小结 现在很多同行做java开发几年了,被迫停留…

[附源码]java毕业设计竞价拍卖系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

灵雀云ACP 斩获“2022金边奖-最佳云原生边缘云平台”

近日,由边缘计算社区主办的全球边缘计算大会上海站成功召开,灵雀云凭借出色的全栈云原生技术实力、专业的高品质服务以及在边缘云场景的丰富落地实践,斩获“2022金边奖-最佳云原生边缘云平台”奖项。 “十四五”规划中明确指出要“协同发展云…

使用kubeadm部署kubernetes集群

文章目录环境环境初始化配置hosts配置时钟同步禁用firewalld、selinux、postfix禁用swap分区开启IP转发、修改内核信息配置IPVS安装Docker配置yum源安装docker-ce配置镜像加速器安装kubernetes组件配置yum源安装kubeadm、kubelet、kubectl工具配置containerd部署master安装pod网…

UTF-8、Unicode编码与汉字的相关内容

介绍 UTF-8是Unicode的一种实现方式,比如一个汉字用Unicode编码表示是两个字节,而用UTF8编码表示则为3个字节。 之所以写这篇文章,是因为我的webserver程序中,浏览器发送资源请求且该资源名为中文时出现了编码问题。 UTF8编码 U…

【SQL 中级语法 2】自连接的用法

SQL的连接运算根据其特征的不同,有着不同的名称,如内连接、外连接、交叉连接等。一般来说,这些连接大都是以不同的表或视图为对象进行的,但针对相同的表或相同的视图的连接也并没有被禁止。针对相同的表进行的连接被称为“自连接”…

python之排序

目录1. 对一维array中的数值进行从大到小排序2. 将DataFrame的列逆序排列3. 根据字符串中的数字进行排序3.2 啊4. 列表参考资料1. 对一维array中的数值进行从大到小排序 import numpy as np a np.array([5,6,8,2,1,7,5,3,90,78,62,5,4,2,9,4]) # b a.sort(axis0,kindquickso…

抑制剂拮抗剂等小分子化合物

小分子化合物主要通过调节其蛋白靶点的活性发挥作用。目前小分子化合物的蛋白靶点主要包括酶、离子通道和受体三大类。根据靶点种类的不同,小分子化合物发挥着不同的作用。 1. 酶的抑制剂(enzyme inhibitor) 在所有的小分子化合物中&#xff…

【虹科案例】虹科脉冲发生器在半导体行业中的应用

非易失性存储单元特点 存储器研究的趋势是开发一种称为非易失性 RAM 的新型存储器,它将 RAM 的速度与大容量存储器的数据存储相结合。几年来有许多新单元类型的提议,例如 FeRAM(铁电存储器)、ReRAM(电阻式存储器&…

C++string类的模拟实现以及经验分享

文章目录1. 为什么学习string类?1.1 C语言中的字符串1.2 两个面试题2. string类的实现构造函数:拷贝构造函数赋值运算符重载:析构函数流提取运算符重载1. 为什么学习string类? 1.1 C语言中的字符串 C语言中,字符串是…

5款十分小众,却又非常好用的良心软件

今天推荐5款十分小众的软件,知道的人不多,但是每个都是非常非常好用的,有兴趣的小伙伴可以自行搜索下载。 1.杀毒软件——火绒安全软件 首先说一下国产杀软之光,这是一款电脑安全软件,病毒库更新及时,界面…

【文档+视频】Verdi基础教程

目录 前言 1.Verdi 环境配置 2.VCS 产生Verdi 波形 1、tb中加入相应的系统函数 2、makefile中加入相应的选项 3.nTrace 1、如何调用Verdi? 【重点】 2、如何查看包含的设计架构? 3、如何查寻模块实例化的位置?【重点】 4、在nTrace…

Linux网络编程11——简单的web服务器

学习视频链接 02-web大练习的概述_bilibili_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1iJ411S7UA/?p132&spm_id_frompageDriver&vd_source0471cde1c644648fafd07b54e303c905 目录 一、项目展示 二、HTTP 协议基础 2.1 HTTP协议基础。 2.2 请求消息(R…