Java的文件操作和IO

news2024/11/24 7:28:18

目录

一、认识文件

树型结构组织 和 目录

文件路径(Path)

其他知识

二、Java 中操作文件

File 概述

属性

构造方法

文件内容的读写 —— 数据流

InputStream 概述

FileInputStream 概述

构造方法

代码示例

利用 Scanner 进行字符读取

OutputStream 概述

方法

说明

利用 OutputStreamWriter 进行字符写入

利用 PrintWriter 找到我们熟悉的方法

三、小程序练习

示例1


一、认识文件

我们先来认识狭义上的文件 (file) 。针对硬盘这种持久化存储的 I/O 设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公桌上的一份份真实的文件一般。

文件除了有数据内容之外,还有一部分信息,例如文件名、文件类型、文件大小等并不作为文件的数据而存在,我们把这部分信息可以视为文件的元信息。

树型结构组织 和 目录

同时,随着文件越来越多,对文件的系统管理也被提上了日程,如何进行文件的组织呢,一种合乎自然 的想法出现了,就是按照层级结构进行组织 —— 也就是我们数据结构中学习过的树形结构。这样,一 种专门用来存放管理信息的特殊文件诞生了,也就是我们平时所谓文件夹(folder) 或者目录 (directory) 的 概念。

文件路径(Path

如何在文件系统中如何定位我们的一个唯一的文件就成为当前要解决的问题,但这难不倒计算机科学 家,因为从树型结构的角度来看,树中的每个结点都可以被一条从根开始,一直到达的结点的路径所描 述,而 这种描述方式就被称为文件的绝对路径(absolute path)

除了可以从根开 始进行路径的描述,我们可以从任意结点出发,进行路径的描述,而这种描述方式就被称为 相对路径(relative path) ,相对于当前所在结点的一条路径。

其他知识

即使是普通文件,根据其保存数据的不同,也经常被分为不同的类型,我们一般简单的划分为文本文件二进制文件,分别指代保存被字符集编码的文本和按照标准格式保存的非被字符集编码过的文件。

 

 Windows 操作系统上,会按照文件名中的后缀来确定文件类型以及该类型文件的默认打开程序。但这 个习俗并不是通用的,在 OSXUnixLinux 等操作系统上,就没有这样的习惯,一般不对文件类型做如此精确地分类。

文件由于被操作系统进行了管理,所以根据不同的用户,会赋予用户不同的对待该文件的权限,一般地 可以认为有可读、可写、可执行权限。
Windows 操作系统上,还有一类文件比较特殊,就是平时我们看到的快捷方式( shortcut ),这种文件只是对真实文件的一种引用而已。其他操作系统上也有类似的概念,例如,软链接(soft link )等。
最后,很多操作系统为了实现接口的统一性,将所有的 I/O 设备都抽象成了文件的概念,使用这一理念最为知名的就是 Unix Linux 操作系统 —— 万物皆文件。

二、Java 中操作文件

本节内容中,我们主要涉及文件的元信息、路径的操作,暂时不涉及关于文件中内容的读写操作。
Java 中通过 java.io.File 来对一个文件(包括目录)进行抽象的描述。注意,有 File 对象,并不代表真实存在该文件。

File 概述

我们先来看看 File 类中的常见属性、构造方法和方法

属性

修饰符及类型属性说明
static StringpathSeparator依赖于系统的路径分隔符,String 类型的表示
static char pathSeparator依赖于系统的路径分隔符,char 类型的表示

构造方法

签名
说明
File(File parent, String
child)
根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(String pathname)
根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File(String parent, String
child)
根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示

文件内容的读写 —— 数据流

InputStream 概述

方法

修饰符及
返回值类
方法签名
说明
intread()
读取一个字节的数据,返回 -1 代表已经完全读完了
intread(byte[] b)
最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1代表已经读完了
int
read(byte[] b, int off, int len)
最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了
voidclose()关闭字节流
说明
InputStream 只是一个抽象类,要使用还需要具体的实现类。关于 InputStream 的实现类有很多,基本可以认为不同的输入设备都可以对应一个 InputStream 类,我们现在只关心从文件中读取,所以使用 FileInputStream。

FileInputStream 概述

构造方法

签名
说明
FileInputStream(File file)
利用 File 构造文件输入流
FileInputStream(String name)
利用文件路径构造文件输入流

代码示例

示例 1
将文件完全读完的两种方式。相比较而言,后一种的 IO 次数更少,性能更好
import java.io.*;
// 需要先在项目目录下准备好一个 hello.txt 的文件,里面填充 "Hello" 的内容
public class Main {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
            while (true) {
                int b = is.read();
                if (b == -1) {
                    // 代表文件已经全部读完
                    break;
               }
                
                System.out.printf("%c", b);
           }
       }
   }
}
import java.io.*;
// 需要先在项目目录下准备好一个 hello.txt 的文件,里面填充 "Hello" 的内容
public class Main {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
            byte[] buf = new byte[1024];
            int len;
            
            while (true) {
                len = is.read(buf);
                if (len == -1) {
                    // 代表文件已经全部读完
                    break;
               }
                
                for (int i = 0; i < len; i++) {
               System.out.printf("%c", buf[i]);
               }
           }
       }
   }
}

示例2
这里我们把文件内容中填充中文看看,注意,写中文的时候使用 UTF-8 编码。 hello.txt 中填写 " 你好中国"
注意:这里我利用了这几个中文的 UTF-8 编码后长度刚好是 3 个字节和长度不超过 1024 字节的现状,但这种方式并不是通用的
import java.io.*;
// 需要先在项目目录下准备好一个 hello.txt 的文件,里面填充 "你好中国" 的内容
public class Main {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
            byte[] buf = new byte[1024];
            int len;
            while (true) {
                len = is.read(buf);
                if (len == -1) {
                    // 代表文件已经全部读完
                    break;
               }
                // 每次使用 3 字节进行 utf-8 解码,得到中文字符
                // 利用 String 中的构造方法完成
                // 这个方法了解下即可,不是通用的解决办法
                for (int i = 0; i < len; i += 3) {
                    String s = new String(buf, i, 3, "UTF-8");
                    System.out.printf("%s", s);
               }
           }
       }
   }
}

利用 Scanner 进行字符读取

上述例子中,我们看到了对字符类型直接使用 InputStream 进行读取是非常麻烦且困难的,所以,我们使用一种我们之前比较熟悉的类来完成该工作,就是 Scanner 类。
构造方法
说明
Scanner(InputStream is, String charset)
使用 charset 字符集进行 is 的扫描读取
示例1
import java.io.*;
import java.util.*;
// 需要先在项目目录下准备好一个 hello.txt 的文件,里面填充 "你好中国" 的内容
public class Main {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
           try (Scanner scanner = new Scanner(is, "UTF-8")) {
               while (scanner.hasNext()) {
                   String s = scanner.next();
                   System.out.print(s);
               }
           }
       }
   }
}

OutputStream 概述

方法

修饰 符及 返回 值类
方法签名
说明
void
write(int b)
写入要给字节的数据
void
write(byte[] b)
b 这个字符数组中的数据全部写入 os
int
write(byte[] b, int off, int len)
b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len
void
close()
关闭字节流
void
flush()
重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的
一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的
数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush (刷新)操作,将数据刷到设备中。

说明

OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中,所以使用 FileOutputStream

利用 OutputStreamWriter 进行字符写入

示例1

import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            os.write('H');
            os.write('e');
            os.write('l');
            os.write('l');
            os.write('o');
            // 不要忘记 flush
            os.flush();
       }
   }
}
import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            byte[] b = new byte[] {
               (byte)'G', (byte)'o', (byte)'o', (byte)'d'
           };
            os.write(b);
          
            // 不要忘记 flush
            os.flush();
       }
   }
}

利用 PrintWriter 找到我们熟悉的方法

上述,我们其实已经完成输出工作,但总是有所不方便,我们接来下将 OutputStream 处理下,使用PrintWriter 类来完成输出,因为PrintWriter 类中提供了我们熟悉的 print/println/printf 方法
OutputStream os = ...;
OutputStreamWriter osWriter = new OutputStreamWriter(os, "utf-8"); // 告诉它,我
们的字符集编码是 utf-8 的
PrintWriter writer = new PrintWriter(osWriter);
// 接下来我们就可以方便的使用 writer 提供的各种方法了
writer.print("Hello");
writer.println("你好");
writer.printf("%d: %s\n", 1, "没什么");
// 不要忘记 flush
writer.flush();

 示例1

import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            try (OutputStreamWriter osWriter = new OutputStreamWriter(os, "UTF-
8")) {
                try (PrintWriter writer = new PrintWriter(osWriter)) {
                    writer.println("我是第一行");
                    writer.print("我的第二行\r\n");
                    writer.printf("%d: 我的第三行\r\n", 1 + 1);
                    writer.flush();
               }
           }
       }
   }
}

三、小程序练习

我们学会了文件的基本操作 + 文件内容读写操作,接下来,我们实现一些小工具程序,来锻炼我们的能力。

示例1

        扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
public class Exe01 {
    public static void main(String[] args) throws IOException {
        // 1. 接收用户输入的扫描路径
        System.out.println("请输入要扫描的路径(绝对路径):");
        Scanner scanner = new Scanner(System.in);
        String rootPath = scanner.next();
        // 2. 判断路径是否有效
        File root = new File(rootPath);
        // 2.1 路径是否存在
        if (!root.exists()) {
            System.out.println("路径不存在");
            return;
        }
        // 2.2 判断File是不是一个目录
        if (!root.isDirectory()) {
            System.out.println("指定的路径不是一个有效目录");
            return;
        }
        // 3. 接收关键字
        System.out.println("请输入关键字");
        String key = scanner.next();
        if (key == null || "".equals(key)) {
            System.out.println("关键字不能为为");
            return;
        }
        // 4. 扫描目录下的所有文件
        scan(root, key);
    }

    private static void scan(File root, String key) throws IOException {
        // 1. 先获取root下的所有文件,包括目录
        File[] files = root.listFiles();
        // 递归的终止条件
        if (files == null || files.length == 0) {
            return;
        }
        // 遍历数组中的每个文件
        for (int i = 0; i < files.length; i++) {
            // 取出每一个文件
            File tempFile = files[i];
            // 判断是文件还是目录
            if (tempFile.isFile()) {
                // 如果是文件,判断文件名中是否包含关键字
                String fileName = tempFile.getName();
                // 如果在文件名中找到关键字
                if (fileName.contains(key)) {
                    System.out.println("找到文件:" + tempFile.getCanonicalPath() + ", 是否删除(Y/N)");
                    // 接收用户的输入,根据输入判断是否删除
                    Scanner scanner = new Scanner(System.in);
                    String order = scanner.next();
                    // 删除操作
                    if (order.equalsIgnoreCase("y")) {
                        tempFile.delete();
                        System.out.println(tempFile.getCanonicalPath() + " 删除成功.");
                    }
                }
            } else {
                // 如果是目录则递归
                scan(tempFile, key);
            }
        }
    }
}

示例2

进行普通文件的复制
public class Ext02 {
    public static void main(String[] args) {
        // 1 . 接收用户输入的源文件路径
        System.out.println("请输入源文件路径(绝对路径):");
        Scanner scanner = new Scanner(System.in);
        String sourcePath = scanner.next();
        // 2. 判断源文件路径是否有效
        File sourceFile = new File(sourcePath);
        // 2.1 文件是否存在
        if (!sourceFile.exists()) {
            System.out.println("源文件不存在");
            return;
        }
        // 2.2 判断是不是一个文件
        if (!sourceFile.isFile()) {
            System.out.println("源文件不是一个有效的文件");
            return;
        }
        // 3. 接收用户输入的目标文件路径
        System.out.println("请输入目标文件路径(绝对路径)");
        String destPath = scanner.next();
        File destFile = new File(destPath);
        // 3.1 判断目标文件是否存在
        if (destFile.exists()) {
            System.out.println("目标文件已存在");
            return;
        }
        // 3.2 判断目标文件的父目录是否存在
        if (!destFile.getParentFile().exists()) {
            System.out.println("目标文件的父目录不存在");
            return;
        }
        // 循环读取源文件的内容并写到目标文件中
        try (FileInputStream inputStream = new FileInputStream(sourceFile);
            FileOutputStream outputStream = new FileOutputStream(destFile)) {
            // 定义一个byte数组用来做为输出型参数,保存每次读到的文件内容
            byte [] bytes = new byte[1024];
            // 循环读取内容
            while (true) {
                int len = inputStream.read(bytes);
                if (len == -1) {
                    break;
                }
                // 写义目标文件
                outputStream.write(bytes);
                // 强制刷新缓冲区
                outputStream.flush();
            }
            System.out.println("复制成功");

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

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

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

相关文章

Puppeteer 部署 - Docker容器 - Idea一键部署

Puppeteer 代码注意 部署到服务器&#xff0c;报错 Running as root without --no-sandbox is not supported.解决方案&#xff1a; const browser await puppeteer.launch({args: [--no-sandbox],env: {DISPLAY: ":10.0"}});Dockerfile 编写Dockerfile根据自己…

前后端交互 | 传递参数的方式

目录 GET请求两种方式 第一种&#xff1a;&#xff1f;& 形式 第二种:不使用&#xff1f;或者引号进行 传递参数 POST请求 小结&#xff1a; get和post的使用情况 传参数重复 传参数&#xff1a;目前共有三种传递参数的方式 GET请求两种方式 第一种&#xff1a;&…

史上最全BI知识全解,万字长文带你读懂BI

在各种新趋势的影响下&#xff0c;数字化不仅在当下有着强大的实力以及巨大的潜力&#xff0c;更是成为了未来世界的标志&#xff0c;成为了社会各界对未来发展的共识。企业为了执行数字化战略&#xff0c;实行数字化转型&#xff0c;实现数据价值&#xff0c;除了需要相关数字…

cpp综合项目—机房预约系统

目录 1、机房预约系统需求 1.1、 身份简介 1.2、机房简介 1.3、申请简介 1.4、 系统具体需求 2、创建项目 3、创建主菜单 3.1 菜单实现 3.2、接口实现 4、退出功能实现 4.1、退出功能实现 4.2、测试退出 5、创建身份类 5.1、身份的基类 5.2、学生类 5.3、老师类…

windows重装后恢复谷歌浏览器数据(旧的用户数据仍存在user data)

背景及注意前提&#xff01;&#xff01;&#xff01; 首先基于我笔记本被公司电脑加域之后&#xff0c;原先家庭版还被升级成了专业版&#xff0c;但是旧的user用户数据还都存在。只是变成了两个用户&#xff0c;开机默认是加域后的用户。 如果你的C盘已经完全被重写了&…

shell计算天数差和月份差

计算月份差 monthdiff.sh function getMonthDiff() { date1${1//-/} date1_year${date1:0:4} date1_month${date1:4:2} date2${2//-/} date2_year${date2:0:4} date2_month${date2:4:2} diff$(((date1_year-date2_year)*12 (date1_month-date2_m…

目前国内有哪些开源的非 SaaS 团队协作平台、项目管理工具呢?

以下是一些常用的开源团队协作平台和项目管理工具&#xff1a; 1.Gitea Gitea 是一个轻量级的 Git 服务&#xff0c;它提供了一个基于 Web 的界面&#xff0c;以方便地管理 Git 存储库。Gitea 也是一个自托管的 Git 服务&#xff0c;可以轻松地在本地或云环境中部署。 2.GitL…

详细分析置换算法

对于操作系统而言&#xff0c;虚拟空间是非常大的&#xff0c;我们往往无法直接将如此大的空间装入内存&#xff0c;而即使我们采用多级页表与段页式存储即使&#xff0c;也仅仅只是节省了页表的大小&#xff0c;如此将如何多的物理页装进内存仍然是一个问题&#xff0c;为此科…

【2023年4月美赛加赛】Z题:The future of Olympics 25页完整论文

【2023年4月美赛加赛】Z题&#xff1a;The future of Olympics 25页完整论文 1 题目 背景 国际奥委会(IOC)正面临着夏季奥运会和冬季奥运会申办数量的减少**[1]**。在过去&#xff0c;举办奥运会的竞争非常激烈&#xff0c;声望也很高。然而&#xff0c;最近&#xff0c;主办…

MySQL基础篇——MySQL数据库 表的操作,

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.表操作 1.表操作-查询创建 1&#xff09;查询当前数据库所有表 2). 查看…

在ubuntu20.x上修改mysql密码

1&#xff0c;在安装mysql后测试mysql是否安装成功可链接数据测试输入一下命令命令都在终端中输入 mysql -u root -p 提示输入密码错误 输入命令后需要输入数据库密码&#xff0c;如忘记密码可进行数据库密码的修改 2&#xff0c;sudo cat /etc/mysql/debian.cnf 输入命令后…

MySQL2-多表查询、子查询、union、limit机制

一、多表查询 在实际开发中&#xff0c;大部分情况下都不是从单表中查询数据&#xff0c;一般都是多张表联合查询取出最终的结果。一般一个业务都会对应多张表。 1.表的连接方式的分类 内连接&#xff1a;等值连接、非等值连接、自连接 外连接&#xff1a;左外连接&#xff08;…

HTML+CSS实训——Day02——写一个登陆界面

前言 今天要继续完成我们的音乐软件了&#xff0c;昨天写完了封面&#xff0c;今天该完成开屏广告和登陆界面了。 登陆界面代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-C…

00后太卷了,老油条感叹真干不过...

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&…

Whatsns内容付费seo优化带采集和熊掌号运营问答系统

正文&#xff1a; 付费课程增加付费课程试听功能基础版和高级企业版&#xff0c;Plus版增加微信消息模板回答通知&#xff0c;采纳答案通知改进兼容因导入Discuz用户密码规则不兼容导致登录失败问题基础版和高级企业版&#xff0c;Plus版增加改进微信文本回复&#xff0c;支持…

【搭建服务器】Win10 IIS搭建webdav服务以及公网访问教程 - 挂载webdav

文章目录 1. 安装IIS必要WebDav组件2. 客户端测试3. 使用cpolar内网穿透&#xff0c;将WebDav服务暴露在公网3.1 打开Web-UI管理界面3.2 创建隧道3.3 查看在线隧道列表 4. 公网远程访问4.1 浏览器访问测试4.2 映射本地盘符访问4.3 安装Raidrive客户端 总结&#xff1a; 自己用W…

你真的会写测试用例吗?如何写?测试用例之中的细节拉满了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 测试用例中的细节…

进程间通信之system V共享内存

目录 &#x1f39e;一、共享内存---shm 1.1shmget ①ftok得到一个key ②shmget得到shm ③shm的性质 1.2 shmctl ①ipcrm ②shmctl 1.3shmat&&shmdt ①shmat ②shmdt 1.4通过shm完成进程间通信 1.5shm的特点 ①shm共享内存的优点 ②shm的缺点 1.6shm的内核…

78.建立一个Web应用程序的布局第二部分

上节课中&#xff0c;我们实现的页面如下图所示&#xff1a; 而最终的页面如下图所示&#xff1a; ● 首先我们 先添加menu的按钮 <menu><button>New</button><button>Reply</button><button>Forward</button><button>Mar…

中兴新支点操作系统电力主站和变电站安全操作系统解决方案

近日&#xff0c;由中国软件行业协会主办的“第二届中国国际软件发展大会”在北京国家会议中心成功召开&#xff0c;国家部委领导、两院院士、行业领袖、龙头企业代表等齐聚一堂&#xff0c;剖析行业热点、分享趋势前瞻。中兴新支点操作系统凭借在国网和南网广泛应用的出色表现…