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

文件操作和IO

文章目录

    • @[toc]
    • 一、文件的含义
      • 1. 狭义的文件
      • 2. 广义的文件
      • 3. Java中的标准输入输出
    • 二、文件的类型
      • 1. 文本文件 vs 二进制文件
      • 2. 判断文件类型的方法
      • 3. 编码方式(字符集)
      • 4. C语言文件操作举例
      • 5. Java对文本文件的处理
    • 三、文件的操作分类
      • 1. 文件系统操作
      • 2. 文件内容操作
      • 3. 各语言支持
    • 四、Java File类详解
      • 1. File类的作用
      • 2. 常用构造方法
      • 3. 常用API方法
      • 4. 路径与分隔符
      • 5. 相对路径的基准目录
    • 五、权限与临时文件
    • 六、目录递归遍历
    • 七、文件内容操作(IO流基础)
      • 1. IO流的概念
      • 2. IO流分类
    • 八、文件内容的操作
      • 1. 打开文件
      • 2. 关闭文件
        • 关闭文件的三种写法
      • 3. 读取文件
        • InputStream
        • FileInputStream
        • Reader
        • Scanner
        • OutputStream
    • 九、小程序练习
      • 1. 扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否要删除该⽂件
      • 2. 普通⽂件的复制
      • 3. 扫描指定⽬录,并找到名称或者内容中包含指定字符的所有普通⽂件(不包含⽬录)
    • 十、OJ题高效输入方案(IO优化)
      • 1. 问题背景
      • 2. 优化思路
      • 3. 快速输入类模板
      • 4. 优化原理
      • 5. 实际应用

一、文件的含义

1. 狭义的文件

  • 定义:硬盘上的文件(如 .txt.docx)及其保存文件的目录(即“文件夹”)。
  • 文件夹 ≈ 目录

2. 广义的文件

  • 定义:操作系统中,很多硬件设备和软件资源都被当作“文件”来管理。
  • 举例
    • 标准输入(键盘):stdin
    • 标准输出(控制台):stdout
    • 打印机、网卡等设备

3. Java中的标准输入输出

  • 标准输入System.in
  • 标准输出System.out

二、文件的类型

1. 文本文件 vs 二进制文件

类型特点举例
文本文件只包含可直接阅读的字符,能用记事本等直接打开.txt.java.c
二进制文件内容不可直接阅读,包含非文本数据,直接打开会乱码.exe.mp3.docx.class
  • 本质:所有文件底层都是二进制数据,文本文件能通过字符编码表(如UTF-8、GBK)正确转换为字符。

2. 判断文件类型的方法

  • 简单粗暴法:用记事本打开,能正常阅读为文本文件,否则为二进制文件。

3. 编码方式(字符集)

  • 文本文件需指定字符编码(如UTF-8/GBK)才能正确解析内容。

4. C语言文件操作举例

  • fopen("c:/test.txt", "r"):文本模式
  • fopen("c:/test.txt", "rb"):二进制模式

5. Java对文本文件的处理

  • Java读取文本文件时会自动进行编码转换,将底层二进制内容转为字符。
  • 读取二进制文件则没有这种转换。

三、文件的操作分类

1. 文件系统操作

  • 创建/删除文件、创建/删除目录、重命名、判断文件是否存在等。

2. 文件内容操作

  • 读文件、写文件。

3. 各语言支持

  • C语言:标准库不直接支持文件系统操作,需调用操作系统API。
  • C++:2017年后标准库支持文件系统操作。
  • JavaFile类专门用于文件系统操作。

四、Java File类详解

1. File类的作用

  • 用于表示硬盘上的文件或目录(无论是否存在)。

2. 常用构造方法

构造方法说明
File(String pathname)通过路径创建File对象(可绝对、相对路径)
File(File parent, String child)父目录对象+子路径
File(String parent, String child)父目录路径+子路径

3. 常用API方法

方法说明
getParent()返回父目录路径
getName()返回文件名
getPath()返回文件路径
getAbsolutePath()返回绝对路径
getCanonicalPath()返回规范化绝对路径
exists()判断文件是否真实存在
isDirectory()是否为目录
isFile()是否为普通文件
createNewFile()创建文件
delete()删除文件
deleteOnExit()JVM结束时删除文件(常用于临时文件)
list()返回目录下所有文件名字符串数组
listFiles()返回目录下所有File对象数组
mkdir()/mkdirs()创建目录/连同中间目录
renameTo(File dest)文件重命名
canRead()/canWrite()判断读/写权限

4. 路径与分隔符

  • Windows\
  • Linux/Mac/
  • Java提供File.separator自动适配不同系统。

5. 相对路径的基准目录

  • IDEA中运行:项目根目录
  • 命令行运行:当前命令行所在目录
  • 被其他进程调用:父进程目录
  • 可用API修改:可通过相关API手动指定

五、权限与临时文件

  • 常见异常
    • 空间不足(硬盘满)
    • 权限不足(无读/写权限)
  • deleteOnExit():常用于临时文件,JVM结束时自动删除。
  • 实际应用:如word未保存时自动生成的临时文件。

六、目录递归遍历

  • list()listFiles():仅遍历当前目录
  • 递归遍历:要遍历所有子目录及文件,需递归实现
void listAll(File dir) {File[] files = dir.listFiles();for(File f : files) {if(f.isDirectory()) {listAll(f); // 递归} else {System.out.println(f.getAbsolutePath());}}
}

七、文件内容操作(IO流基础)

1. IO流的概念

  • :数据像水流一样流动,按需分批读写。

  • 把数据视为池中的水

    • 写 - 视为通过水龙头灌水 - 输出流
    • 读 - 视为水龙头接水 - 输入流
  • 计算机中的输入输出,都是以 cpu 视角来谈的

    • 数据远离 cpu 就是输出
    • 数据靠近 cpu 就是输入

2. IO流分类

分类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter
适用场景二进制数据(图片、音频等)文本数据(txt、csv等)
  • InputStream/OutputStream结尾:字节流

  • Reader/Writer结尾:字符流

  • 资源泄露问题:不易察觉,长期积累会导致系统异常,养成良好关闭习惯。


八、文件内容的操作

1. 打开文件

InputStream inputStream = new FileInputStream("./test.txt");
  • 每打开一个文件,进程的文件描述符表会增加一个表项,资源有限。
  • 未及时关闭文件会导致“文件资源泄露”,影响长期运行的程序。

2. 关闭文件

  • **close()**方法释放文件描述符,防止资源泄露。
关闭文件的三种写法

(1) 直接close

适用于简单场景,进程即将结束。

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

(2) try-finally保证关闭

严谨做法,确保异常时也能释放资源。

InputStream inputStream = null;
try {inputStream = new FileInputStream("./test.txt");// 读写操作
} catch (IOException e) {e.printStackTrace();
} finally {try {if (inputStream != null) inputStream.close();} catch (IOException e) {throw new RuntimeException(e);}
}

(3) try-with-resources(推荐)

自动调用close,简洁安全,前提是实现了Closeable接口。

try() 里面可以写多个对象的构造过程

try (InputStream inputStream = new FileInputStream("./test.txt")) {// 读写操作
} catch (IOException e) {e.printStackTrace();
}

3. 读取文件

InputStream

方法

修饰符及返回值类型方法签名说明
intread()读取一个字节的数据,返回 -1 代表已经完全读完了
intread(byte[] b)最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1 代表以及读完了
intread(byte[] b, int off, int len)最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了
voidclose()关闭字节流

InputStream 只是一个抽象类,要使用还需要具体的实现类。

关于 InputStream 的实现类有很多,基本可以认为不同的输入设备都可以对应一个 InputStream 类。我们现在只关心从文件中读取,所以使用 FileInputStream

FileInputStream

构造方法

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

读取文件的两种方法.相比较而言,后一种的IO次数更好,性能更好.

public static void main(String[] args) throws IOException {try(InputStream inputStream = new FileInputStream("./test.txt")) {while (true){int b=inputStream.read();if (b==-1){break;}System.out.printf("0x%x\n",b);}}
}
public static void main(String[] args) throws IOException {try(InputStream inputStream=new FileInputStream("./test.txt")){while (true){byte[] buffer=new byte[1024];int n= inputStream.read(buffer);if (n==-1){break;}for (int i = 0; i < n; i++) {System.out.printf("0x%x\n",buffer[i]);}}}
}
Reader

Reader也是相同的用法,一次读一个字符

public static void main(String[] args) throws IOException {try(Reader reader=new FileReader("./test.txt")){while (true){char[] buffer=new char[1024];int n= reader.read(buffer);if (n==-1){break;}for (int i = 0; i < n; i++) {System.out.println(buffer[i]);}}}
}

Reader是按字节读取(UTF-8编码)

  • 每个汉字是 3 个字节

而保存是 Java 中的 char

  • 每个 char 是 2 个字节

  • 当用 char 表示汉字时,不再使用 UTF-8,而是使用 Unicode

  • 在 Unicode 中,一个汉字就是 2 个字节

  • 使用字符流读取数据时,Java 标准库会自动根据数据的编码进行转码。

Scanner

Scanner 里面可以填写文件的 InputStream

public static void main(String[] args) throws IOException {try(InputStream inputStream=new FileInputStream("./test.txt")){try (Scanner scanner =new Scanner(inputStream)){while (scanner.hasNext()){String s=scanner.next();System.out.println(s);}}}
}
OutputStream
修饰符及返回值类型方法签名说明
voidwrite(int b)写入一个字节的数据
voidwrite(byte[] b)将字节数组 b 中的所有数据写入输出流
intwrite(byte[] b, int off, int len)将字节数组 b 从 off 开始的 len 个数据写入输出流
voidclose()关闭字节流
voidflush()刷新缓冲区,将数据真正写入设备中。 说明:I/O 速度慢,OutputStream 通常会用缓冲区临时存储数据,只有缓冲区满或调用 flush 时才真正写入设备。写完或合适时需调用 flush 以防数据遗留在缓冲区。
  • OutputStream 只是一个抽象类,实际使用时需要具体实现类。
  • 目前只关心写入文件,则使用 FileOutputStream
public static void main(String[] args) throws IOException {// 要写入的数据String data = "你好,OutputStream!";// 将字符串转换为字节数组(UTF-8编码)byte[] bytes = data.getBytes();// 创建输出流,写入到文件 "output.txt"try (FileOutputStream fos = new FileOutputStream("output.txt")) {// 1. 写入一个字节(这里只写第一个字节)fos.write(bytes[0]);// 2. 写入整个字节数组fos.write(bytes);// 3. 写入字节数组的一部分(比如从第2个字节开始,写3个字节)fos.write(bytes, 1, 3);// 4. 刷新缓冲区fos.flush();// 5. 关闭流(try-with-resources会自动关闭)System.out.println("写入完成!");} catch (IOException e) {e.printStackTrace();}
}

默认情况下,FileOutputStream会覆盖原有文件内容。如果你希望\追加内容而不是覆盖,可以使用它的一个特殊构造方法(append)。

FileOutputStream fos = new FileOutputStream("output.txt", true);

九、小程序练习

1. 扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否要删除该⽂件

    public static void scan(File directory,String key,Scanner scanner){File[] files=directory.listFiles();for (File file:files){if (file==null){return;}// 如果是目录,则继续遍历if (file.isDirectory()){scan(file,key,scanner);}else{// 如果是文件,则开始查询String name=file.getName();if (name.contains(key)){System.out.println(name+"是否要删除此文件:Y/N");String choise=scanner.next();if (choise.equals("Y")||choise.equals("y")){file.delete();System.out.println("文件已删除");}else{System.out.println("文件未删除");}}}}}public static void main(String[] args) throws IOException {File directory =new File("C:\\Users\\24314\\Desktop\\test");try(Scanner scanner=new Scanner(System.in)){scan(directory,"test",scanner);}}

2. 普通⽂件的复制

public static void main(String[] args) throws IOException {// 普通文件的复制File srcFile = new File("C:\\Users\\24314\\Desktop\\test\\怠美.jpg");File destFile = new File("C:\\Users\\24314\\Desktop\\test\\怠美-复制.jpg");// 判断源文件是否存在if (!srcFile.exists()){System.out.println("源文件不存在");return;}// 判断复制文件是否存在if (destFile.exists()){System.out.println("目标文件已存在,将被覆盖");return;}// 读取源文件try(InputStream inputStream= new FileInputStream(srcFile);OutputStream outputStream=new FileOutputStream(destFile)){byte[] buffer=new byte[1024];while (true){int n=inputStream.read(buffer);if (n==-1){break;}outputStream.write(buffer,0,n);}}
}

3. 扫描指定⽬录,并找到名称或者内容中包含指定字符的所有普通⽂件(不包含⽬录)

public static void scan(File directory,String key) throws IOException {File[] files=directory.listFiles();if (files==null){return;}for (File file:files){if (file.isDirectory()){scan(file,key);}else{try(Reader reader=new FileReader(file)){char[] buffer=new char[1024];StringBuilder tmp=new StringBuilder();while (true){int n= reader.read(buffer);if (n==-1){break;}tmp.append(buffer,0,n);}if (tmp.toString().contains(key)){System.out.println("文件"+file.getName()+":"+tmp);System.out.println(file.getName());}}}}
}
public static void main(String[] args) throws IOException {File directory =new File("C:\\Users\\24314\\Desktop\\test");if (!directory.isDirectory()){System.out.println("目录不存在");return;}String key="test";scan(directory,key);
}

十、OJ题高效输入方案(IO优化)

1. 问题背景

  • 使用Scanner时,数据量大容易超时——因为每次next都是一次IO操作。

2. 优化思路

  • BufferedReader一次读入一整行,再用StringTokenizer分词,大幅减少IO次数

3. 快速输入类模板

class Read {StringTokenizer st = new StringTokenizer("");BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));String next() throws IOException {while (!st.hasMoreTokens()) {st = new StringTokenizer(bf.readLine());}return st.nextToken();}String nextLine() throws IOException {return bf.readLine();}int nextInt() throws IOException {return Integer.parseInt(next());}long nextLong() throws IOException {return Long.parseLong(next());}double nextDouble() throws IOException {return Double.parseDouble(next());}
}

4. 优化原理

  • Scanner:每次next都是一次IO,慢。
  • BufferedReader+StringTokenizer:一次IO可获得多条数据,极大提升效率。

5. 实际应用

  • OJ题、数据量大的输入场景,推荐使用该方案。

文章转载自:

http://AeDB0xeW.zjcmr.cn
http://5a0iL319.zjcmr.cn
http://wuuuoIdr.zjcmr.cn
http://WMWKRY4G.zjcmr.cn
http://MJaxyUrw.zjcmr.cn
http://cm4s5SNX.zjcmr.cn
http://HaIweCEB.zjcmr.cn
http://8oFpTuf3.zjcmr.cn
http://uydmXSOZ.zjcmr.cn
http://j9mGzscX.zjcmr.cn
http://iJWBzbup.zjcmr.cn
http://eZlOe4NB.zjcmr.cn
http://Fyw6thrH.zjcmr.cn
http://bB6pRi4o.zjcmr.cn
http://WdUrW0F7.zjcmr.cn
http://O4qa0nWg.zjcmr.cn
http://sCwGrJZk.zjcmr.cn
http://3Vf06xxs.zjcmr.cn
http://HYzLSUmF.zjcmr.cn
http://rJfhbgp5.zjcmr.cn
http://3NpJhPji.zjcmr.cn
http://zPgqKiSl.zjcmr.cn
http://UP7d4zAc.zjcmr.cn
http://CdDGoEFV.zjcmr.cn
http://qAfD9jmA.zjcmr.cn
http://QoL1pmpQ.zjcmr.cn
http://s0ULTjSd.zjcmr.cn
http://pfXoJXSE.zjcmr.cn
http://51e36Nlr.zjcmr.cn
http://A5K49eFV.zjcmr.cn
http://www.dtcms.com/a/370581.html

相关文章:

  • leetcode LCR 159 库存管理III
  • 使用 TCMalloc 检查内存使用情况和内存泄漏
  • Altium Designer(AD24)加载License文件方法
  • 【Gigascience】时空转录组测序探索小鼠心脏发育的细胞与分子基础
  • Ubuntu:Git SSH密钥配置的完整流程
  • 智能驾驶调研
  • 【Luogu_P8118】 「RdOI R3.5」Mystery【Slope Trick】【DP】
  • SSH服务远程安全登录
  • cds序列转换为pepperl脚本详细解读及使用
  • 什么时候用no,什么时候用non,什么时候用not?
  • 2025年本体论:公理与规则的挑战与趋势
  • 发布vue项目、nginx配置及问题场景(history)
  • Netty从0到1系列之EventLoop
  • 在Ubuntu上配置Nginx实现开机自启功能
  • 智慧灌区系统:科技赋能,让农田灌溉更智能、更高效、更可持续
  • 第2课:环境搭建:基于DeepSeek API的开发环境配置
  • 本地MOCK
  • 使用Docker安装Stirling-PDF(PDF工具)
  • 交换机详细
  • 综合安防集成系统解决方案,智慧园区,智慧小区安防方案(300页Word方案)
  • Spring Data JPA 对PostgreSQL向量数据的支持
  • 去中心化投票系统开发教程 第二章:开发环境搭建
  • BOSS直聘招聘端自动化识别策略调整(20250905)
  • MySQL--索引和事务
  • c++之基础B(双重循环)(第五课)
  • 3、工厂模式
  • 2025高教社国赛数学建模竞赛B题完整参考论文(含模型和代码)
  • MCP Token超限问题解决方案
  • 并行编程实战——CUDA编程的纹理内存
  • 京东商品评论API开发指南