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

文件输入与输出io

文件

文章目录

  • 文件
    • 硬盘与内存的区别
    • 绝对路径与相对路径
    • 文件操作
      • File类的构造方法
    • File类中的方法
    • 文件内容的读写
      • 流的分类
      • 字节流
        • 读文件
        • 写文件
      • 字符流
        • 读文件
        • 写文件
    • 练习一
    • 练习二
    • 练习三

狭义的文件: 存储在硬盘上的数据 , “以文件为单位”, 进行组织

常见的文件类型: 文本文件,图片 视频文件 音频文件 可执行程序

文件夹也叫做"目录"它也还是一种特殊的文件

硬盘与内存的区别

  1. 硬盘的存储空间比较大,内存的存储空间比较小
  2. 硬盘的访问速度慢,内存的访问速度快
  3. 硬盘的成本比较低 , 内存的成本比较高
  4. 硬盘的数据断电不会消失, 内存的数据断电后会消失(硬盘的持久性存储)

广义的文件

操作系统是负责管理软硬件的系统, Linux往往会把资源都抽象成"文件"

这就是"一切皆文件"

硬件也可以看做是文件

比如,一个网卡,可以把这个网卡抽象成一个文件, 创建出一个特殊的文件, 表示网卡

从网卡中接收数据就是读这个文件.

向网卡中发送数据就是写这个文件

目录结构是一个N叉树

绝对路径与相对路径

绝对路径是一个很具体的独一无二的路径

举一个例子:

image-20220930100114634

C:\Program Files (x86)\MySQL\MySQL Installer for Windows 这就是一个绝对路径

其中D: 是盘符 , \ 分割是部分都是目录

相对路径

相对路径首先要有一个"基准路径"也叫做"工作路径"

相对路径是以基准路径为起点,按照指示, 逐步找到目标的路径表示方式

基准路径不一样, 相对路径就不会完全相同

如果基准路径是C: .\Program Files (x86)\MySQL\MySQL Installer for Windows就是相对路径

如果基准路径是C:\Program Files (x86), 那么 .\MySQL\MySQL Installer for Windows就是相对路径

谈到相对路径, 一定要明确基准路径是什么

文件操作

文件操作是属于操作系统层面,提供的一些API, 不同的操作系统中的文件API是不一样的

Java是一个能跨平台的语言,为了统一代码, 在JVM中 把不同系统的操作文件的API都进行了封装, 这样子Java就能使用Java中的库来操作文件了

在Java中操作文件的类是File类

File类是在java.io这个包里面的

所谓的io就是input和output的缩写

输入输出的对象是CPU或者内存

File类的构造方法

image-20220930101941080

主要是第二个方法,就是直接传入一个路径字符串,这个路径可以是绝对路径也可以是相对 路径

File类中的方法

接下来举几个File类中方法的例子

package io;

import java.io.File;
import java.io.IOException;

public class Demo1 {
    public static void main(String[] args) throws IOException {
         File f = new File("D:/test.txt");//传入的是绝对路径
        System.out.println(f.getParent());//获取盘符
        System.out.println(f.getName());//获取文件名
        System.out.println(f.getPath());//获取传入的文件路径
        System.out.println(f.getAbsolutePath());//获取文件绝对路径
        System.out.println(f.getCanonicalFile());//获取文件的相对路径
        
    }
}

image-20220930103657167

当传的是绝对路径的时候,后面三个路径好像没有什么区别

当传入的是相对路径就会不一样了

package io;

import java.io.File;
import java.io.IOException;

public class Demo1 {
    public static void main(String[] args) throws IOException {
         File f = new File("./test.txt");//传入的是相对路径
        System.out.println(f.getParent());//获取盘符
        System.out.println(f.getName());//获取文件名
        System.out.println(f.getPath());//获取文件路径(传入的路径)
        System.out.println(f.getAbsolutePath());//获取文件绝对路径
        System.out.println(f.getCanonicalFile());//获取文件的相对路径

    }
}

image-20220930103908546

使用IDEA运行这段程序,基准目录是项目所在的路径

注意: File里传入的文件路径,文件不一定是真实存在的

所以要创建出文件

package io;

import java.io.File;
import java.io.IOException;

public class Demo2 {
    public static void main(String[] args) throws IOException {
        File f = new File("./test.txt");
        //判断文件是否存在
        System.out.println(f.exists());
        //判断文件是否是普通文件
        System.out.println(f.isFile());
        //判断文件是否是目录
        System.out.println(f.isDirectory());

        f.createNewFile();//将文件创建出来
        System.out.println(f.exists());
        System.out.println(f.isFile());
        System.out.println(f.isDirectory());
    }

}

image-20220930104837712

删除文件

image-20220930105231185

创建目录(单层 / 多层)

image-20220930105627498

image-20220930105837887

打印同级目录

import java.io.File;
import java.util.Arrays;

public class Demo6 {
    public static void main(String[] args) {
        File f = new File("./test.dir");
        String[]  res = f .list();
        System.out.println(Arrays.toString(res));
    }
}

image-20220930111029612

上述的操作都是在操作"文件系统",而不是真正对一个文件进行操作

针对文件的内容进行操作才是关键, 也就是读文件和写文件

文件内容的读写

Java中 将读写文件的操作比喻成"流", stream

水的特点是,流动起来,绵延不断

对应到文件的读写操作上, 要是想要写100个字节到文件中, 可以一次写1个字节,分100次写,也可以一次写10个字节,分10次写,还可以一次写20个字节,分5次写,所以这是很灵活的

Java标准库中,就在"流"的概念上,提供了一组类,来完成读写文件的操作

流的分类

image-20220930113423339

字节流

读文件

InputStream有三个构造方法

image-20220930163741455

一次读一个字节,读出的结果是read的返回值

image-20220930163747658

image-20220930163755355

这两个方法, 会把读的内容放到字节数组b中

注意: 这里传入的数组b是存放输出结果的内容,此时 b叫做输出型参数,这在java中不是很常见

package io;

import java.io.*;

public class Demo7 {
    public static void main(String[] args) throws IOException {
        //打开文件,要想进行文件的读写,首先要打开文件
        //InputStream是抽象类,不能被直接实例化
        InputStream inputStream = new FileInputStream("./test.dir./heihei.txt");
        while(true){
            //read()读取结束的时候,会返回一个-1
            int ret = inputStream.read();//一个一个读
            if(ret == -1){
                break;
            }
            System.out.println(ret);
        }
        inputStream.close();//关闭文件,一定要记得关闭
    }
}

heihei.txt 的内容是Are you ok, 所以最后程序输出的结果是ASCII码:(字节流)

image-20221001132119930

写文件

image-20221001132641892

write有三个构造方法,write(int b)是写入一个字节

write(byte[] b)是把字节数组中的所有内容写到文件中

write(byte[] b, int off ,int len) 是把b字节数组从线标off位置开始写,写进去len个字节

package io;

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

public class Demo8 {
    //字节流  写文件
    public static void main(String[] args) throws IOException {
        //写的时候,只要打开文件,原来的内容 就没有了
        OutputStream outputStream = new FileOutputStream("./test.dir/heihei.txt");
        outputStream.write(97);
        outputStream.write(99);
        outputStream.write(100);
        outputStream.close();//关闭文件
    }
}

最后heihei.txt 的内容是 acd

字符流

读文件

package io;

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

public class Demo9 {
    public static void main(String[] args) throws IOException {
        Reader reader = new FileReader("./test.dir/heihei.txt");
        while (true){
            //read()返回的是int类型,但是Reader是字符流,所以要强转
            int ret = reader.read();
            if(ret == -1){
                break;
            }
            char ch = (char) ret;
            System.out.println(ch);
        }
        reader.close();//关闭文件
    }
}

写文件

image-20221001135050673

写文件的时候可以从传入String字符串

package io;


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

public class Demo10 {
    public static void main(String[] args) throws IOException {
        Writer writer = new FileWriter("./test.dir/heihei.txt");
        writer.write("hello world");    
        writer.close();
    }
}

其实读文件还有别的方法–使用scanner

package io;

import javax.swing.plaf.SeparatorUI;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class Demo11 {
    public static void main(String[] args) throws IOException {
        //使用scanner来读取文件
        InputStream inputStream = new FileInputStream("./test.dir/heihei.txt");
        Scanner scanner = new Scanner(inputStream);//把文件传给scanner
        while (scanner.hasNext()){
            System.out.println(scanner.next());
        }
        inputStream.close();
    }
}

打开文件之后,一定要记得关闭文件,否则会造成很严重的问题

具体原理: 每个进程对应着一个PCB(双向链表),也可能十多个,PCB里面有一个字段–文件描述符表, 同一个进程中的多个PCB共用一份 文件描述符表 , 每次打开一个文件, 就会在表中创建一个项(相当于数组,最大长度是有上限的),关闭文件就会释放掉这个项, 如果持续打开文件,但是从来不关闭文件,就会导致表项被耗尽,之后就不能再打开别的文件了,这就是文件资源泄漏,十分严重,因为这个问题不是一下子就暴露的,而是需要时间才会出现bug

image-20221001143012752

在这个代码中,不一定能保证 一定能执行到关闭文件,万一前面抛出异常,关闭文件就执行不到了

解决方法

使用try finally, 但是这样写比较丑

public static void main(String[] args) throws IOException {
        InputStream inputStream = null;
        try{
            inputStream = new FileInputStream("./test.dir/heihei.txt");
            Scanner scanner = new Scanner(inputStream);
            while (scanner.hasNext()) {
                System.out.println(scanner.next());
            }
        }finally{
            inputStream.close();
        }
    }

使用try with resources , 在try结束之后,就会自动调用对象的close(),而且还能传入多个对象,只要 用分号连接就行

 public static void main(String[] args) throws FileNotFoundException {
        try(InputStream inputStream = new FileInputStream("./test.dir/heihei.txt")){
            Scanner scanner = new Scanner(inputStream);
            while (scanner.hasNext()){
                System.out.println(scanner.next());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

以上就是所有的关于io的知识讲解,其实不多也不难,知识需要多多练习

接下来就要进行一些练习

练习一

扫描指定目录, 并找到包含指定字符的所有普通文件(不包含目录),找到之后询问用户是否需要删除该文件

思路:

1.用户输入扫描的目录和删除的关键字

2.遍历所有的目录

3.找到包含关键字的文件,选择是否删除

遍历的时候,要先判断当前目录是不是空的,确定非空之后,再进行遍历

使用 listFiles 可以把当前目录中的文件和子目录都列举出来,但是没办法列举出子目录的子目录, 所以解决办法就是遍历listFiles的结果, 针对每一个元素看看它是普通文件还是子目录,要是是普通文件就选择是否删除,要是是子目录,就进行递归,继续扫描子目录的子目录

image-20221001153724262

也就是说listFiles只能看一级的目录,可以使用递归来扫描下一级的内容

目录本质是就是"树"

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

public class Test1 {
    //1. 输入要查找的目录和要删除的关键字
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你要查找的目录");
        File rootDir = new File(scanner.next());//rootDir是要扫描的目录
        if(!rootDir.isDirectory()){
            System.out.println("输入的不是目录");
            return;
        }
        //输入的是目录
        System.out.println("请输入你要删除的关键字: ");
        String toDelete = scanner.next();
        scanDir(rootDir,toDelete);
    }

    //2. 遍历所有的目录
    private static void scanDir(File rootDir, String toDelete) {
        System.out.println("当前访问"+ rootDir.getAbsolutePath());
        File[] files = rootDir.listFiles();
        if(files == null){
            //目录是空的
            return;
        }//判断空目录一定不要省去,因为这是递归的结束条件
        //目录不是空的,就要开始遍历
        for(File f : files){
            if(f.isFile()){
                checkDelete(f,toDelete);
            }else{
                //在这个非空目录中不是普通文件,只能是又一级目录
                scanDir(f,toDelete);//递归查看下一级目录
            }
        }
    }

    private static void checkDelete(File f, String toDelete) {
        if(f.getName().contains(toDelete)){//查看文件名中是否包含关键字
            System.out.println(toDelete + "已经在" + f.getAbsolutePath()+ "中找到,请问是否删除?(Y/N)");
            Scanner scanner = new Scanner(System.in);
            String ret = scanner.next();
            if(ret.equals("y") || ret.equals("Y")){
                f.delete();
            }
        }
    }
}

image-20221001154018768

练习二

实现文件的复制

实现复制比较简单,就是打开一个文件读,一个文件写

package io;

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

public class Test2 {
    public static void main(String[] args) {
        //1.用户输入要复制的文件名(源文件)和复制后的文件名(目标文件)
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你要复制的文件名路径");
        File srcFile = new File(scanner.next());
        System.out.println("请输入复制后的文件名路径");
        File destFile = new File(scanner.next());
        //要确保源文件是一个普通文件,例如d:/tmp/1.txt
        if(!srcFile.isFile()){
            System.out.println("输入的源文件有误");
        }
        //要确保文件的上一级是一个目录,这样子才好创建,例如d:/tmp/12.txt
        if(!destFile.getParentFile().isDirectory()){
            System.out.println("输入的目标文件有误");
        }
        //打开源文件和目标文件
        try(InputStream inputStream = new FileInputStream(srcFile);
            OutputStream outputStream = new FileOutputStream(destFile)){
            //源文件一个一个字节地读,目标文件一个一个字节地写
            while (true){
              int ret =  inputStream.read();
              if(ret == -1){
                  break;
              }
              outputStream.write(ret);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

image-20221001160402058

练习三

扫描指定目录 , 并找到名称或者内容包含指定字符的所有普通文件

这个算是练习一 的进阶版,主要是增加了在文件内容中查找的程序

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

public class Test3 {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你要查找的目录");
        File file = new File(scanner.next());
        if(!file.isDirectory()){
            System.out.println("你输入的不是目录");
            return;
        }
        System.out.println("请输入你要查找的关键字");
        String toFind = scanner.next();
        scanDir(file,toFind);//遍历目录
    }
    private static void scanDir(File rootDir, String toFind) throws IOException {
        System.out.println("当前访问"+rootDir.getAbsolutePath());
        File[] files = rootDir.listFiles();
        if(files == null){
            return;
        }
        for(File f : files){
            if(f.isFile()){
                checkFile(f,toFind);
            }else{
                //说明还有下一级目录
                scanDir(f,toFind);
            }
        }
    }

    private static  void checkFile(File file, String toFind) throws IOException {
        //1. 检查文件名
        if(file.getName().contains(toFind)){
            System.out.println(file.getCanonicalFile()+"在文件名中"+"包含"+toFind);
        }
        //2. 检查文件内容
        try(InputStream inputStream = new FileInputStream(file)){  //打开文件
            StringBuffer stringBuffer = new StringBuffer();
            Scanner scanner = new Scanner(inputStream);//读取文件
            while (scanner.hasNextLine()) {
                stringBuffer.append(scanner.nextLine() );//添加所有的内容到stringBuffer
                }
                if(stringBuffer.indexOf(toFind)>-1){  //在stringBuffer里面找,要是找不到就返回-1
                    System.out.println(file.getCanonicalFile()+"在文件内容里中包含"+toFind);
            }
        }
    }
}

这里的练习三代码的运行效率是很低的

相关文章:

  • SpringBoot整合Docker实现一次构建到处运行
  • 程序员眼中的Linux操作系统——初识指令
  • process.env.NODE_ENV与@vue/cli-service及其.env.*默认外部环境配置文件之跨域部署
  • MySQL:用户权限和语言接口
  • 2022年广西壮族自治区中职网络安全技能竞赛“Linux操作系统渗透测试详解”
  • 沉睡者 - 抖音中视频计划横版16:9视频制作教程
  • 强大的包管理器Poetry
  • MFC Windows 程序设计[304]之文件夹的枚举(附源码)
  • 【ArcGIS微课1000例】0033:地图点状标记符号设计教程
  • 神经网络中的算法-BN算法
  • SpringBoot接收参数的几种常用方式
  • Linux--权限
  • ESP32上手指南
  • 多径信道下通过LMS均衡算法提高通信质量——详细版
  • 前端笔试/面试题
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • eclipse的离线汉化
  • HTTP--网络协议分层,http历史(二)
  • PHP变量
  • spring学习第二天
  • 第2章 网络文档
  • 检测对象或数组
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 手机端车牌号码键盘的vue组件
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 物联网链路协议
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • raise 与 raise ... from 的区别
  • (2)nginx 安装、启停
  • (zt)最盛行的警世狂言(爆笑)
  • (补)B+树一些思想
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (附源码)ssm智慧社区管理系统 毕业设计 101635
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (三)c52学习之旅-点亮LED灯
  • (四) Graphivz 颜色选择
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转)ObjectiveC 深浅拷贝学习
  • (转)如何上传第三方jar包至Maven私服让maven项目可以使用第三方jar包
  • (转)我也是一只IT小小鸟
  • ****Linux下Mysql的安装和配置
  • .gitignore文件---让git自动忽略指定文件
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET BackgroundWorker
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .NET业务框架的构建
  • /bin/bash^M: bad interpreter: No such file or directory
  • @vue/cli 3.x+引入jQuery
  • [ACM] hdu 1201 18岁生日
  • [Angular] 笔记 20:NgContent
  • [Angular] 笔记 9:list/detail 页面以及@Output
  • [AR]Vumark(下一代条形码)
  • [dart学习]第四篇:函数
  • [EFI]Dell Inspiron 15 5567 电脑 Hackintosh 黑苹果efi引导文件
  • [ES-5.6.12] x-pack ssl