javaEE 初阶 — 文件内容的读写

news2025/1/13 13:47:57

文章目录

  • 数据流
  • 1. 字节流
    • 1.1 InputStream 概述
      • 1.1.1 无参数 read 的使用
      • 1.1.2 一个参数 read 的使用
    • 1.2 使用 OutputStream 写文件
      • 1.2.1 对于关闭文件的解释
  • 2. 字符流
    • 2.1 Reader 概述
      • 2.1.1 read 方法的使用
    • 2.2 Writer 概述
      • 2.2.1 write 的使用
    • 2.3 Scanner 补充

数据流


针对文件内容,使用 “流对象” 进行操作的。

如果现在通过水龙头接 100ml 的水,可以一次接 100ml,也可以一次接10ml 分 10 次接完。
再比如说我兜里有 100 块钱,我可以一次花完,也可以分多次花完。
这就叫做 “流”。

在一个羽毛球桶里,只能一个一个的拿羽毛球,而不能把羽毛球分为半个拿,这就不是 “流”。

如果要从一个文件中读 100 个字节,就可以一次读 100 个字节,一次读完。
也可以分多次读完,这是可以随心所欲的读的。

因此就把读写文件相关的对象,称为 “流对象”

java 标准库里的流对象,从类型上分成两个大类:

  • 字节流 这是操作二级制数据的
    例如:InputStream、OutputStream、FileInputStream、FileOutputStream
  • 字符流 这是操作文本数据的
    例如:Reader、Writer、FileReader、FileWriter


这些类的使用方式是非常固定的。

核心操作只有4个:

  • 打卡文件。(构造对象)
  • 关闭文件。(close)
  • 读文件。(read)针对 InputStream 或者 Reader
  • 写文件。(write)针对 OutoutStream 或者 Writer

1. 字节流

1.1 InputStream 概述




InputStream、OutputStream、Reader、Writer 都是抽象类,不能直接 new。

而是要 new FileInputStream

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


FileInputStream 需要 FileNotFoundException 这个异常。
FileNotFoundException 这是一个 IOException 的子类。


  • read 方法无参数版本:一次只读一个字节
  • read 方法一个参数版本:把读到的内容填充到参数的这个字节数组当中。(此处的参数是个“输出型参数”,返回值是实际读取的是字节数)
  • read 方法三个参数版本:和 2 类似,只不过是往数组的一部分区间里尽可能填充

1.1.1 无参数 read 的使用




read 读取的是一个字节,按理来说,返回一个 byte 就行了,但是实际上返回的是 int 。

除了要表示 byte 里的 0~255(-128 ~ 127)这样的情况之外,还需要表示一个特殊的情况。
那就是读取文件结束(文件读到末尾了)的情况,要使用 -1 表示。


代码

package io;

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

public class IoDemo6 {

    //使用字节流来读取文件
    public static void main(String[] args) throws IOException {
        //创建 InputStream 的时候,可以使用绝对和相对路径,也可以使用 File 对象
        InputStream inputStream = new FileInputStream("d:/test.txt");

        //进行读操作
        while (true) {
            int getFile = inputStream.read(); //读取到一个字节
            if (getFile == -1) {
                // 读取完毕
                break;
            }
            System.out.println("" + (byte)getFile);
        }

        inputStream.close(); //关闭文件
    }
}




test.txt 文件里的内容是 hello 。



读到的每个字节,就是一个打印出来的数字。这些数字就是 hello 的 ascii 码。

这里的 txt 文件使用字节流也是可以读的,但是不够方便,更希望使用的是一个一个字符读的字符流

1.1.2 一个参数 read 的使用

public static void main(String[] args) throws IOException {
    //创建 InputStream 的时候,可以使用绝对和相对路径,也可以使用 File 对象
    InputStream inputStream = new FileInputStream("d:/test.txt");

    //进行读操作
    while (true) {
        byte[] bytes = new byte[1024];
        int len = inputStream.read(bytes);
        if ( len == -1) {
            // 读完了
            break;
        }
        //此时读取的结果就被放到数组当中了
        for (int i = 0; i < len; i++) {
            System.out.println(" " + (byte)bytes[i]);
        }
    }
    inputStream.close(); //关闭文件
}


一个参数的 read 需要调用者提前准备好一个数组。

  int len = inputStream.read(bytes);


这里的传参操作,相当于是把刚才准好的数组交给 read 方法。
让 read 方法内部针对这个数组进行天填写。(此处传参相当于是 “输出型参数”)

java 中一般习惯做法是,输入的信息作为参数,输出的信息作为返回值。



可以看到与上述读的结果是一致的,三个参数的版本和 2 类似,也就不再赘述。

read 会尽可能的把参数传进来的数组给填满。

上面的数组长度是 1024 ,read 就会尽可能的读取 1024 个字节填到数组中。
但是实际上,文件剩余长度是有限的,如果剩余长度超过了 1024 ,此时 1024 个字节都会填满,返回值就只是 1024 了。

如果剩余的长度不足 1024 ,此时有多少就填多少,read 方法就会返回当前实际读取的长度。

举个例子



可以看到 d 盘中有一个图片

public static void main(String[] args) throws IOException {
        //创建 InputStream 的时候,可以使用绝对和相对路径,也可以使用 File 对象
        InputStream inputStream = new FileInputStream("d:/javaEE.jpg");

        //进行读操作
        while (true) {
            byte[] bytes = new byte[1024];
            int len = inputStream.read(bytes);
            System.out.println("len:" + len);
            if ( len == -1) {
                // 读完了
                break;
            }
        }
        inputStream.close(); //关闭文件
    }




整个文件的大小是 17 kb ,而数组的大小是 1024 个字节,因此就要多次循环来进行 read 操作。
刚开始读取的时候,每次读取的长度都是 1024 个字节。(第二轮读的数据会覆盖第一轮的数据)

随着数据的读取,文件的大小不足 1024 个字节,这个时候能读多少就读多少。
这里剩下的是 158 个字节,别的文件可能会不同。

把这 158 个字节读完以后,文件就读到了末尾,再一次进入循环,文件就无内容可读了。
就会返回 -1 结束循环。


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

单次 IO 操作,是要访问硬盘/IO设备,单词操作是比较消耗时间的。
如果频繁的进行单次 IO 操作,消耗的时间就更多了。

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

无参数版本的代码,是一次读一个字节,循环的次数就比较多,read 的次数也很高。
一个参数的版本,是一次读 1024 个字节,循环次数就降低了很多,read 的次数也变少了。

1.2 使用 OutputStream 写文件


下面来使用 OutputStream 写文件

 OutputStream outputStream = new FileOutputStream("d:/test.txt");


OutputStream 只能 new FileOutputStream

对于 OutputStream 来说,默认情况下打卡一个文件就会文件原有的内容。

如果不想清空,流对象还提供了一个 “追加写” 对象,通过这个就可以实现不清空文件就把新内容追加写到后面。



write 方法也是有三个版本的,这里和 read 方法使用类似,这里不再赘述。

off 表示要从数组的哪个下标开始写,len 表示要写多长。



可以看到文件原来的内容是 hello

package io;

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

public class IoDem7 {

    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(); //关闭文件
    }
}


执行程序后



a c b d 对应的 ascii 码值就是 97 98 99 100。

1.2.1 对于关闭文件的解释


在内核当中,使用 PCB 这样的数据结构表示进程。

一个线程对应一个 PCB ,一个进程可以对应一个 PCB 也可以对应多个。

PCB 中有一个重要的属性,文件描述符表,(相当于是一个数组)记录了该进程打开了哪些文件。
(即使一个进程里有多个线程多个 PCB 也没关系,这些 PCB 会共用同一个文件描述符表)



每次打开文件操作,就会在文件描述符表中,申请一个位置,把这个信息放进去。
每次关闭文件,也就会把这个文件描述符表对应的表象给释放掉。

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

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

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




如何才能保证 close 被执行


下面的是推荐的写法

 public static void main(String[] args) throws IOException {
     try(OutputStream outputStream = new FileOutputStream("d:/test.txt")) {
         //开始写文件
         outputStream.write(97);
         outputStream.write(98);
         outputStream.write(99);
         outputStream.write(100);
     }
 }


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

这个语法在 Java,被称为 try with resources

try with resources 不是随便哪一个对象 放到 try() 里就能自动释放的,需要满足一定的要求。



可以看到只有实现了 Closeable 这个接口的类才可以放到 try() 中被自动关闭。

这个方法提供的方法就是 close 方法。

2. 字符流

2.1 Reader 概述


Reader 里的也有 read 方法只不过比 InputStream 里的多了一个。



无参版本也是一次读一个字节,char[] cbuf 参数和三个参数的版本和 InputStream 里的一样。
(可以读一个字符数组 或者 一次读一个结果到字符数组的一部分)

虽然返回值类型是个 int ,但是实际上返回的是 char 。

真正有区别的是 CharBuffer target 参数的版本。
这个版本不做介绍,其实就是相当于是把这个字符数组分装了一下。

2.1.1 read 方法的使用


可以看到文件里是 a b c d。

下面演示无参版本。

package io;

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

public class IoDemo8 {

    public static void main(String[] args) throws IOException {
        //可以使用绝对和相对路径,也可以使用 File 对象
        try (Reader reader = new FileReader("d:/test.txt")){
            while (true) {
                //读操作
                int ch = reader.read(); //一次读一个字节
                if (ch == -1) {
                    //文件读完了
                    break;
                }
                System.out.println("" + (char)ch);
            }
        }
    }
}




执行程序后也看到了a b c d。

如果文件里是中文也是可以被读出来的。

2.2 Writer 概述


Writer 有以下几个版本的 write。



int c 参数和 char[] cbuf 参数版本可以一次写一个字符也可以一次写一个字符数组。
String str 参数版本可以写一个字符串。
带字符数组的三个参数版本可以写到字符数组的一部分。
带字符串的三个参数版本可以写到字符串的一部分

2.2.1 write 的使用




文件内容本身是 hello 。

下面演示写一个字符串的版本。

package io;

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

public class IoDemo9 {

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


执行程序后发生更改。

2.3 Scanner 补充

 Scanner scanner = new Scanner(System.in);




System.in 其实就是一个流对象,所以还可以写成下面的形式。

package io;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class IoDemo10 {

    public static void main(String[] args) {
        try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
            Scanner scanner = new Scanner(inputStream);
            
            // 此时读的就是文件里的内容
            scanner.next();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


如果 Scanner 里面填的是标准输入的流对象,那就是从键盘读;如果填的是文件的流对象,那就是从文件读。

Scanner 的 close 本质上是要关闭内部包含的这个流对象。

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

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

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

相关文章

Cookie和Session以及相关的方法

作者&#xff1a;~小明学编程 文章专栏&#xff1a;JavaEE 格言&#xff1a;热爱编程的&#xff0c;终将被编程所厚爱。 目录 Cookie 什么是Cookie? 为什么要用Cookie? Cookie的原理 Session 什么是Session? 为什么要用Session? Session原理 核心方法 实现用户登…

【Linux编译器】

前言 打怪升级第&#xff1a;22天 一、gcc、g 背景知识&#xff1a; gcc和g都是Linux中的编译器&#xff0c;其中gcc只可以编译C语言文件&#xff0c;g可以编译c文件&#xff0c;当然也可以编译C语言文件&#xff08;c兼容C语言嘛&#xff09;。 由于两者的操作选项基本一样&a…

转换函数和运算符类

我们以下是采用内联函数来进行的#ifndef ___Class_Counter #if 1 #endif ___Class_counter #include <climits> class Counter{unsigned cnt; // unsigned mmm; public:Counter() : cnt(0) {}//构造函数初始化器//Counter(double mmm):mmm(2){}/*void increment() {i…

低代码平台助力汽车行业构筑新基建

编者按&#xff1a;本文分析了汽车行业数字化转型的需求&#xff0c;并列举了汽车行业利用低代码平台数字化转型的场景&#xff0c;最后通过华晨汽车案例展示了低代码在实现业务模板集成方面的表现。关键词&#xff1a;数据集成&#xff0c;低代码平台&#xff0c;汽车行业汽车…

后台生成4位图像验证码

主要依赖&#xff1a; <dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version> </dependency>spring相关依赖参照用到的另行添加。 package com.math.controller;impor…

质心标准差和分散程度

( A, B )---1*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有1个节点&#xff0c;AB各由9张二值化的图片组成&#xff0c;在前述的实验中得到了A全是0&#xff0c;B中有3个1的迭代次数数据。 0 1 2 3 4 5 6 7 8 迭代次数 A-B 1b 1b 1b 0 0 0 0 0 0 54269.648 …

Java 日志框架 JUL

文章目录日志文件的重要性常见日志框架什么是JULJUL架构介绍入门案例JUL日志级别Logger之间的父子关系日志的配置文件日志原理解析日志文件的重要性 做开发最怕的就是线上系统出问题了&#xff0c;轻则留下产品和系统不安全可靠的不好印象&#xff0c;重则影响到公司的收入和口…

②【Spring】一文精通:IOC - 基于XML方式管理Bean

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ IOC操作&#xff1a;基于XML管理Bean一、&…

智公网:公务员行测基础考点

一、法律常识 1. 行政主体要“三有”&#xff0c;一有“权”&#xff08;行政权力&#xff09;&#xff0c;二有“名”&#xff08;以自己名义从事行政管理&#xff09;&#xff0c;三有“责”&#xff08;独立承担行政责任&#xff09;。 2. 受委托的组织和个人不具备行政主…

【自学Python】Python查找字符串

Python查找字符串 Python查找字符串教程 在开发过程中&#xff0c;很多时候我们有在一个 字符串 中查找另一个字符串的需求&#xff0c;在 Python 中&#xff0c;在一个字符串中查找另一个字符串我们使用 find() 函数。 Python find()函数详解 语法 S.find(sub[, start[, …

基于2D Object Detection的目标几何中心三维位置估计(C++)

文章目录 介绍前置知识三维位置估计订阅内参订阅对齐后的深度图并进行后处理源码介绍 这篇博客将简单讲解下如何利用二维目标检测方法和RGBD数据来获取感兴趣目标的几何中心三位位置结果。 前置知识 为了让博客内容显得简洁些,这里不再介绍前置知识,包括: 2D Object Dete…

selenium通过cookie跳过登录验证码实现登录

一般做ui自动化面对验证码一直是一个问题&#xff0c;在网上页百度了一些关于验证码的问题&#xff0c;有通过把验证码提取下来通过pytesser&#xff0c;tesseract&#xff0c;OCR 等库处理&#xff0c;但是这些比较麻烦。 还是因为本人比较渣看不懂&#xff0c;后来想到了weba…

selenium页面元素定位、操作

1 打开 / 关闭浏览器2 打开URL链接3 定位单个页面元素3.1 通过ID定位3.2 通过name定位3.3 通过classname定位3.4 通过CSS定位3.5 通过链接的文本信息定位3.6 通过XPath定位3.6.1 xpath语法3.6.2 实例4 操作4.1 基本操作4.2 切换窗口、框架5 封装selenium的基本操作6 判断元素是…

Express做后端服务详细步骤,从零到一

文章目录一、全局安装脚手架二、生成项目1.生成项目2.目录结构介绍3.拓展&#xff1a;配置文件热更新&#xff08;避免改一次文件重启一次服务&#xff09;步骤1&#xff1a;安装nodemon步骤2&#xff1a;创建nodemon.json文件步骤3&#xff1a;更改启动命令步骤4&#xff1a;上…

如何准确测试相位噪声

相位噪声是表征CW信号频谱纯度的非常重要的参数&#xff0c;衡量了信号频率的短期稳定度。通常所说的相噪为单边带(SSB) 相位噪声&#xff0c;相噪的好坏对于系统的性能至关重要&#xff01; 对于终端通信而言&#xff0c;如果接收机LO的相噪较差&#xff0c;且在信道附近存在…

GO语言开篇-Go语言急速入门(基础知识点)| 青训营笔记

文章目录一.GO语言应用场景1.1 前言1.2 什么是GO语言1.3 仅仅10行完成高并发的服务器二.入门2.1 基础语法-HelloWorld2.2 基础语法-变量类型2.2.1 变量2.2.2 if else2.2.3 循环2.2.4 switch2.2.5 数组2.2.6 切片2.2.7 map2.2.8 range2.2.9 函数2.2.10 指针2.2.11 结构体2.2.12 …

Java基础之《netty(24)—netty入站与出站机制》

一、基本说明 1、netty的组件设计&#xff1a;netty的主要组件有Channel、EventLoop、ChannelFuture、ChannelHandler、ChannelPipe等。 2、ChannelHandler充当了处理入站和出站数据的应用程序逻辑的容器。 例如&#xff0c;实现ChannelInboundHandler接口&#xff08;或Chan…

开关电源详解

目录 电源分类 线性稳压电源&#xff08;LDO&#xff09; 适用场景 特点 基本组成 开关电源 适用场景 发展方向 特点 分类 基本组成&#xff1a; 开关电源和线性电源对比&#xff1a; 两款开关电源的性能参数对比&#xff1a; 某款电源适配器&#xff1a; 电源分类…

Python NumPy 拆分数组

前言NumPy&#xff08;Numerical Python的缩写&#xff09;是一个开源的Python科学计算库。使用NumPy&#xff0c;就可以很自然地使用数组和矩阵。NumPy包含很多实用的数学函数&#xff0c;涵盖线性代数运算、傅里叶变换和随机数生成等功能。本文主要介绍Python NumPy 拆分数组…

2-求和(蓝桥杯)

问题描述给定 nn 个整数 a_1, a_2, , a_na1,a2,⋅⋅⋅,an &#xff0c;求它们两两相乘再相加的和&#xff0c;即&#xff1a;Sa_{1} \cdot a_{2}a_{1} \cdot a_{3}\cdotsa_{1} \cdot a_{n}a_{2} \cdot a_{3}\cdotsa_{n-2} \cdot a_{n-1}a_{n-2} \cdot a_{n}a_{n-1} \cdot a_…