IO详解(文件,流对象,一些练习)

news2024/11/25 4:41:47

目录

文件

文件概念

文件的路径

路径有俩种表示风格 

文件类型

如何区分文本文件还是二进制文件?

java对文件的操作

File类中的一些方法

流对象

流对象的简单概念

java标准库的流对象

1.字节流,(操作二进制数据的)

2.字符流 (操作文本数据的)

流对象最核心的四个操作

注意,intput 和 output的方向

读文件(字节流)

read无参数版本

 read一个参数版本

 理解read的行为和返回值

写文件

关于close

如何确保close被执行到

字符流

读操作

 写操作

 关于Scanner

练习

用户输入一个查询的词,看看当前目录下(以及子目录里)是否有匹配的结果,如果有匹配结果,就询问用户是否删除

把一个文件拷贝成另一个文件(把第一个文件按照字节读取,把结果写入到另一个文件中) 


文件

文件概念

狭义的文件:指的是硬盘上的 文件目录(文件夹)

广义的文件:泛指计算机中的很多 软硬件资源
操作系统中,把很多的硬件设备和软件资源抽象成了文件,按照文件的方式来统一管理.
例如:在网络编程中的网卡,操作系统是把网卡当作一个文件来进行操作的

文件的路径

之前学习的代码中,存储数据,主要靠变量,变量是在内存中的
现在学习的文件,则是在硬盘上

每个文件,在硬盘上都有一个具体的路径

路径有俩种表示风格 

1.绝对路径:以c:d:盘符开头的路径

2.相对路径:以当前所在目录为基准,以.或..开头(.有时可以省略),找到指定路径

当前所在目录:称为工作目录,每个程序运行的时候,都有一个工作目录(在控制台里通过命令操作较为明显)

假定,当前的工作目录是,d:/tmp

tmp目录下有111这个目录,定位到111这个目录,就可以表示成
./111(./就表示的是当前的目录 d:/tmp)

../表示当前目录的上级目录,如果工作目录是d:/tmp/222 想要定位到111这个目录,相对路径写作.
../111(..表示当前目录d:/tmp/222 的上级目录 d:/tmp)

文件类型

如何区分文本文件还是二进制文件?

直接使用 记事本 打开,如果乱码了,说明是二进制文件,如果没乱,就是文本文件

java对文件的操作

1.针对文件系统操作(文件的创建,删除,重命名)

2.针对文件内容操作(文件读和写)

java标准库提供了File这个类

在new File 对象的时候,构造方法参数中,可以指定一个路径.此时File对象代表这个路径对应的文件

File类中的一些方法

方法

 

 代码

  public static void main(String[] args) throws IOException {
        File file = new File("./test.txt");
        System.out.println(file.getName());
        System.out.println(file.getParent());
        System.out.println(file.getAbsoluteFile());
        System.out.println(file.getPath());
        System.out.println(file.getCanonicalFile());
    }

运行结果

方法 

 

 代码

public static void main(String[] args) throws IOException {
        File file = new File("D:/test.txt");
        file.createNewFile();
        System.out.println(file.exists());
        System.out.println(file.isFile());
        System.out.println(file.isDirectory());
    }

运行结果

 方法

代码

public static void main(String[] args) throws IOException {
        File file = new File("D:/test.txt");
        System.out.println(file.exists());
        file.createNewFile();
        System.out.println(file.exists());
        file.delete();
        System.out.println(file.exists());
    }

 运行结果

 方法

代码

 public static void main(String[] args) {
        File file = new File("./test");
        file.mkdir();
    }

 

public static void main(String[] args) {
        File file = new File("./test/aaa/bbb");
        file.mkdirs();
    }

运行结果

 

流对象

流对象的简单概念

针对文件内容,使用"流对象" 进行操作,从文件中读100个字节,我们可以一次读100个字节,一次读完
也可以一次读20个字节,5次读完,我们可以随心所欲的读

java标准库的流对象

从类型上分为俩大类

1.字节流,(操作二进制数据的)

InputStream FileInputStream

OutputStream FileOutputStream

2.字符流 (操作文本数据的)

Reader FileReader

Writer FileWriter

流对象最核心的四个操作

这些类的使用方式是固定的,核心就是四个操作

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

注意,intput 和 output的方向

我们是以CPU为中心,来看待这个方向的
数据朝着CPU的方向流向,就是输入,所以把从硬盘中读取数据到内存 这个过程称为读 input
数据远离CPU的方向流向,就是输入,所以把从内存中写数据到硬盘 这个过程称为写 output

读文件(字节流)

read无参数版本

代码

public static void main(String[] args) throws IOException {
//创建 InputStream 对象的时候,使用绝对路径或者相对路径,都可以,也可以使用File对象
        InputStream inputStream = new FileInputStream("D:/test.txt");
//进行读操作
        while (true){
            int b = inputStream.read();
            if(b == -1){
                break;
            }
            System.out.println(""+(byte)b);
        }
        inputStream.close();
}

 

 

运行结果

D盘中test的文件内容

 

 

 read一个参数版本

   public static void main(String[] args) throws IOException {
        //创建 InputStream 对象的时候,使用绝对路径或者相对路径,都可以,也可以使用File对象
        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;
            }
            for (int i = 0; i < len; i++) {
                System.out.println(""+buffer[i]);
            }
        }
        inputStream.close();
    }

运行结果

 

 理解read的行为和返回值

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

第二个版本的代码有什么好处?

buffer存在的意义,是为了提高IO效率,单次IO操作,是要访问硬盘IO设备,单次操作是比较消耗时间的
如果频繁进行这样的IO操作,耗时比较大

单次IO时间是一定的,如果能缩短IO次数,此时就可以提高程序的整体效率了
第一个版本的代码,是一次读取一个字节,循环次数比较高,read次数很高,读取IO次数也很高
第二个版本的代码,是一次读取1024个字节,循环次数降低了很多,read次数变少了

写文件

代码

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

运行结果

对于OutputStream 来说,默认情况下,打开一个文件,会先清空文件原有的内容(这样,之前的"hello"就被清空了)
如果不想清空,流对象还提供了一个"追加写"对象,通过这个就可以实现不清空文件,把新内容追加到后面.

关于close

对于上述代码中的outputStream.close();这里的close操作,含义是关闭文件
一个线程对于一个PCB,一个进程对应1个或多个PCB
PCB有一个重要的属性,文件描述符表(相当于一个数组),记录了该进程打开了哪些文件.(即使一个进程有多个线程多个PCB也没关系,这些PCB共用一个文件描述符表)

如果没有close,对应的表项,没有及时释放.虽然 java有GC,GC操作会在回收这个outputStream对象的时候完成释放操作,但是这个GC不一定及时...
所以,如果不能手动释放,意味着文件描述符表可能很快就被占满了(文件描述符表这个数组,不能自动扩容,有上限)
如果占满了后,再次打开文件,就会打开失败

 close 一般来说是要执行的,但是如果一个程序,有一些文件对象自始至终都要使用,也可以不用关闭
随着进程结束,PCB销毁了,文件描述符表也就销毁了,对应的资源操作系统就自动回收
了,因此,如果一个文件close之后,程序就立即结束了,此时也可以省略close

有时我们可能会发现,写文件的内容没有真正在文件中出现,很大可能是因为缓存区,
写操作其实是,先写到缓冲区里(缓冲区有很多种形态,自己写的代码里可以有缓冲区,标准库里也可以有缓冲区,操作系统内核里也可以有缓冲区)
写操作执行完了,内容可能在缓冲区,还没有真正进入硬盘,close操作 就会触发缓冲区的刷新(刷新操作就是把缓冲区的内容写到硬盘里)
除了close之外,还可以通过flush方法刷新缓冲区(此时文件不会立即关闭)

如何确保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()里就能自动释放,必须要这个对象实现了Closeable接口

实现了这个Closeable接口的类才可以放到try()中被自动关闭,这个接口提供的方法就是close方法

字符流

读操作

 代码

public static void main(String[] args) throws IOException {
        try(Reader reader = new FileReader("D:/test.txt")){
            while(true){
                int ch = reader.read();
                if(ch == -1){
                    break;
                }
                System.out.println((char)ch+"");
            }
        }
    }

运行结果 

 写操作

 public static void main(String[] args) throws IOException {
        try(Writer writer = new FileWriter("D:/test.txt")){
            writer.write("hello world");
        }
    }

运行结果

 关于Scanner

Scanner是搭配流对象来使用的

代码

public static void main(String[] args) throws IOException {
        try(InputStream inputStream = new FileInputStream("D:test.txt")) {
            //此时读取的内容就是从 文件 中读取了
            Scanner scanner = new Scanner(inputStream);
            scanner.next();
        }
    }

练习

用户输入一个查询的词,看看当前目录下(以及子目录里)是否有匹配的结果,如果有匹配结果,就询问用户是否删除

代码

import java.io.File;
import java.util.Scanner;

public class IODemo10 {
    static Scanner scanner = new Scanner(System.in);
    public static void main(String[] args) {
        //让用户搜索一个指定搜索的目录
        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();
        //针对指定的路径进行扫描,递归操作
        //先从根目录出发
        //判定一下,当前的这个目录里,是否包含我们所需要删除的目录.如果是则删除,否则跳过下一个
        //如果当前目录里包含一些目录,再针对子目录进行递归
        scanDir(root,nameToDelete);
    }

    private static void scanDir(File root, String nameToDelete) {
        //1.先列出 root 下的文件和目录
        File[] files = root.listFiles();
        if(files == null){
            //当前root 目录下没有东西,是一个空目录
            //结束继续递归
            return;
        }
        //2.遍历当前列出的结果
        for(File x : files){
            if(x.isDirectory()){
                //如果是目录,就进一步递归
                 scanDir(x,nameToDelete);
            }else{
                //如果是普通文件,则判定是否要删除
                if(x.getName().contains(nameToDelete)){
                    System.out.println("确实是否要删除: "+x.getAbsolutePath()+"嘛");
                    String choice = scanner.next();
                    if(choice.equals("y")||choice.equals("Y")){
                        x.delete();
                        System.out.println("删除成功");
                    }else{
                        System.out.println("删除取消");
                    }
                }
            }
        }
    }
}

 运行结果

把一个文件拷贝成另一个文件(把第一个文件按照字节读取,把结果写入到另一个文件中) 

代码

import java.io.*;
import java.util.Scanner;

public class IODemo11 {
    public static void main(String[] args) throws IOException {
        //输入俩个路径
        //源 和 目标(从哪里,拷贝到哪里)
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要拷贝哪个文件: ");
        String srcPath = scanner.next();
        System.out.println("请输入要拷贝到哪个地方: ");
        String destPath = scanner.next();
        File srcFile = new File(srcPath);
        if(!srcFile.isFile()){
            //如果源路径不是一个文件(是一个目录,或者不存在)
            //此时不做任何操作
            System.out.println("输入的源路径有误");
            return;
        }
        File destFile = new File(destPath);
        if(destFile.isFile()){
            //如果目标路径已经存在,认为不能拷贝
            System.out.println("当前输入的目标路径有误");
            return;
        }
        //进行拷贝操作
        try(InputStream inputStream = new FileInputStream(srcFile);
            OutputStream outputStream = new FileOutputStream(destFile)){
            //进行读文件操作
            while(true){
                int b = inputStream.read();
                if(b == -1){
                    break;
                }
                //进行写操作
                outputStream.write(b);
            }
        }
    }
}

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

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

相关文章

Android Framework-进程间通信——Binder

我们知道&#xff0c;同一个程序中的两个函数之间能直接调用的根本原因是处于相同的内存空间中。 比如有以下两个函数A和B&#xff1a; /*Simple.c*/ void A() { B(); } void B() { }因为是在一个内存空间中&#xff0c;虚拟地址的映射规则完全一致&#xff0c;所以函数A和B之…

【JAVA程序设计】【C00111】基于SSM的网上图书商城管理系统——有文档

基于SSM的网上图书商城管理系统——有文档项目简介项目获取开发环境项目技术运行截图项目简介 基于ssm框架开发的网上在线图书售卖商城项目&#xff0c;本项目分为三种权限&#xff1a;系统管理员、卖家、买家 管理员角色包含以下功能&#xff1a; 用户信息管理、权限管理、订…

XSS跨站脚本

XSS跨站脚本XSS简介XSS验证XSS危害XSS简介 XSS被称为跨站脚本攻击(Cross-site scripting)&#xff0c;由于和CSS(Cascading Style Sheets)重名&#xff0c;所以改为XSS。XSS主要基于javascript语言完成恶意的攻击行为&#xff0c;因为javascript可以非常灵活的操作html、css和…

day57-day58【代码随想录】二刷数组

文章目录前言一、螺旋矩阵||&#xff08;力扣59&#xff09;二、螺旋矩阵&#xff08;力扣54&#xff09;三、顺时针打印矩阵&#xff08;剑指 Offer29&#xff09;四、在排序数组中查找元素的第一个和最后一个位置&#xff08;力扣34&#xff09;【二分查找】五、有多少小于当…

自动驾驶决策概况

文章目录1. 第一章行为决策在自动驾驶系统架构中的位置2. 行为决策算法的种类2.1 基于规则的决策算法2.1.1 决策树2.1.2 有限状态机&#xff08;FSM&#xff09;2.1.3 基于本体论&#xff08;Ontologies-based&#xff09;2.2 基于统计的决策算法2.2.1 贝叶斯网络&#xff08;B…

智慧制硅厂 Web SCADA 生产线

我国目前是全球最大的工业硅生产国、消费国和贸易国&#xff0c;且未来该产业的主要增量也将来源于我国。绿色低碳发展已成为全球大趋势和国际社会的共识&#xff0c;随着我国“双碳”目标的推进&#xff0c;光伏产业链快速发展&#xff0c;在光伏装机需求的带动下&#xff0c;…

flink兼容性验证

flink介绍&#xff1a;https://blog.csdn.net/weixin_43563705/article/details/107604693 一、安装启动 安装flink及其依赖 yum install java-1.8.0-openjdk curl tar mkdir -p /usr/local/flink wget https://mirrors.aliyun.com/apache/flink/flink-1.16.1/flink-1.16.1-bi…

如何校招进BAT做产品经理

嗨&#xff0c;很高兴&#xff0c;以文字的形式和你见面。在校招中&#xff0c;我拿到了百度、京东、爱奇艺、新浪和去哪儿的产品经理校招offer&#xff0c;其中百度是special offer。在找实习的过程中&#xff0c;也拿到了爱奇艺、微信电影票、搜狐畅游、艺龙等公司的产品经理…

Ac4GlcNAz,98924-81-3,N-乙酰葡糖胺叠氮基,可以进行糖化学修饰

Ac4GlcNAz反应特点&#xff1a;四乙酰氨基叠氮氨基葡萄糖&#xff08;Ac4GlcNAz&#xff0c;98924-81-3&#xff09;为糖缀合物可视化提供非放射性替代物。西安凯新生物科技有限公司供应的解释道它是细胞通透性、细胞内处理和结合的&#xff0c;而不是其天然单糖对乙酰氨基葡萄…

CEC2017:斑马优化算法(Zebra Optimization Algorithm,ZOA)求解cec2017(提供MATLAB代码)

一、斑马优化算法 斑马优化算法&#xff08;Zebra Optimization Algorithm&#xff0c;ZOA&#xff09;Eva Trojovsk等人于2022年提出&#xff0c;其模拟斑马的觅食和对捕食者攻击的防御行为。 斑马因身上有起保护作用的斑纹而得名。没有任何动物比斑马的皮毛更与众不同。斑…

SpringCloud的基本使用

文章目录一.单体架构和分布式架构的区别1.单体架构2.分布式架构二.微服务1.微服务的架构特征2.微服务技术对比3.企业需求4.微服务的远程定义5.提供者与消费者三.Eureka注册中心1.eureka的作用2.搭建EurekaServer3.Eureka注册3.服务拉取四.Ribbon负载均衡1.负载均衡流程2.负载均…

总被程序员坑?你需要了解API接口

编辑导读&#xff1a;程序员是公司里的技术岗&#xff0c;也是产品经理最密切的合作伙伴。但是&#xff0c;程序员能看懂产品经理的工作&#xff0c;产品经理却不一定能明白程序员的工作&#xff0c;因此也常常被无良程序员坑。本文就从API接口的维度&#xff0c;浅析API的概念…

十七、本地方法接口的理解

什么是本地方法? 1.简单来讲&#xff0c;一个Ntive method 就是一个Java调用非Java代码的接口.一个Native Method 是这样一个Java方法:该方法的实现由非Java语言实现&#xff0c;比如C,这个特征并非Java所特有&#xff0c;很多其他的编程语言都由这一机制&#xff0c;比如在C中…

常见HTTP攻击赏析(1)

基于OpenAPI的APIcat开源日志监控软件已经开发一段时间了&#xff0c;在自己的网站上抓到了一些HTTP的攻击&#xff0c;没事&#xff0c;我们就汇总给大家做个赏析&#xff0c;也当是个提醒。 对应的OpenAPI定义上传到了百家饭平台 API攻击样例详情百家饭OpenAPI平台主站http…

最新会声会影2023旗舰版更新了哪些功能?

会声会影在用户的陪伴下走过26余载&#xff0c;经过上百个版本的优化迭代&#xff0c;已将操作极大简易化&#xff0c;会声会影拥有公认的上手口碑。只需将想要的效果拖拽到轨道上&#xff0c;一拖一放间快速成片。专业工具助力视频剪辑操作简单&#xff0c;功能同样强大&#…

Ubuntu 新人上手 Microk8s 指南

文章目录1. 什么是 Ubuntu 核心2. 什么是 Kubernetes3. 什么是MicroK8s4. 为什么选择 Microk8s on Core5. 安装Ubuntu Core6. Ubuntu Core上安装 MicroK8S7. 启动 Microk8s8. 启用必要的 MicroK8s 插件9. 部署示例容器工作负载10. 检查部署状态并访问您的应用程序11. 管理镜像1…

docker布署spring boot jar包项目

目录docker 安装创建目录制作镜像启动容器查看日志docker 安装 Docker安装、详解与部署 创建目录 服务器中创建一个目录&#xff0c;存放项目jar包和Dockerfile 文件 mkdir /目录位置创建目录后创建Dockerfile文件&#xff0c;上传jar包到同一目录下 创建dockerfile vim Doc…

一文读懂Java/O流的使用方法和技巧

1.前言 Java 中的 I/O 流是实现输入和输出的一种机制&#xff0c;可以用来读写文件、网络、内存等各种资源。Java 提供了各种类型的流&#xff0c;包括字节流和字符流&#xff0c;以及面向文本和二进制数据的流。在本文中&#xff0c;我们将深入探讨 Java I/O 流的各个方面&am…

空指针,野指针

空指针在C/C中&#xff0c;空指针&#xff08;null pointer&#xff09;是指向内存地址0的指针变量。NULL在C/C中的定义为&#xff1a;#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif #endif从上面的代码定义中&#xff0c;我们可以发现在C…

【数据聚类|深度聚类】Deep Comprehensive Correlation Mining for Image Clustering(DCCM)论文研读

Abstract 翻译 最近出现的深度无监督方法使我们能够联合学习表示和对未标记数据进行聚类。这些深度聚类方法主要关注样本之间的相关性,例如选择高精度对来逐步调整特征表示,而忽略了其他有用的相关性。本文提出了一种新的聚类框架,称为深度全面相关挖掘(DCCM),从三个方面…