前言
本次内容主要针对java当中的文件读写操作, 文件读写的基本操作包括File对象, InputString, OutputString.及其构造方法, 用法, 使用场景等讲解
java.io.File
对于一个文件, 我们如要使用Java语言对其进行操作, 那么java标准库里面提供了一个File类, 用来描述一个文件, 此处的File类的实例对象就是对应的硬盘上一个文件的抽象, 这个抽象具体指的就是, 文件是存储来硬盘上的, 如果直接用代码操作硬盘是不方便的, 于是就在内存中创建一个与此文件与之对应的文件对象, 便于清楚这个对象的位置, 然后修改这个文件.
1. 构造方法
File类构造方法
方法定义 | 说明 |
File(File parent, String child) | parent为父目录(一个File实例), 然后加上一个孩子路径, 构成一个新的File实例 |
File(String pathName) | 根绝文件路径创建出一个新的File实例, 这个路径可以是绝对路径或者是相对路径 |
File(String parent, String child) | 使用父目录加子目录的形式,创建一个新的File实例. |
我们创建一个文件夹, 在c盘下的文件夹, 然后在文件夹里面创建这个test.txt文本文件, 如图:
- File(String pathName)
import java.io.File; public class Main { public static void main(String[] args) { File file1 = new File("c:/test/test.txt"); boolean ret = file1.exists(); System.out.println(ret); } }
输出: true( 此处的exists()方法是检查这个文件是否存在, 如果不存在返回false)
- File(String parent, String child)
import java.io.File; public class Main { public static void main(String[] args) { File file2 = new File("C:\\c\\code_c","cProject"); boolean ret = file2.exists(); System.out.println(ret); } }
输出:true;
- File(File parent, String child)
import java.io.File; public class Main { public static void main(String[] args) { File file1 = new File("C:\\c\\code_c"); File file2 = new File(file1,"cProject"); boolean ret = file2.exists(); System.out.println(ret); } }
输出:true;
2. 方法
File类中的方法概览
返回类型 | 方法定义 | 说明 |
String |
getParent() | 返回 File 对象的父目录文件路径 |
String | getName() | 返回 FIle 对象的纯文件名称 |
String | getPath() | 返回 File 对象的文件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
boolean | exists() | 判断 File 对象描述的文件/ 目录是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
boolean | createNewFile() | 根据 File 对象,自动创建一个空文件。成功创建后返回 true |
boolean | delete() | 根据 File 对象,删除该文件。成功删除后返回 true |
void | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行 |
String[ ] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[ ] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象表示 |
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
boolean |
renameTo(File dest) |
进行文件改名,也可以视为我们平时的剪切、粘贴操 作 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
对于一个文件:
以下方法的实例都是用上面这个文件(cProject.sln)来表示, 其基准目录为C:/c/code_c/cProject
get类方法
import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file = new File("C:/c/code_c/cProject/cProject.sln"); String retGetParent = file.getParent(); String retGetName = file.getName(); String retGetPath = file.getPath(); String retGetAbsolutePath = file.getAbsolutePath(); String retGetCanonicalPath = file.getCanonicalPath(); System.out.println("文件:C:/c/code_c/cProject/cProject.sln"); System.out.println("父目录文件路径:"+retGetParent); System.out.println("文件名称:"+retGetName); System.out.println("文件路径:"+retGetPath); System.out.println("文件绝对路径:"+retGetAbsolutePath); System.out.println("修饰过的绝对路径"+retGetCanonicalPath); } }
文件的创建和删除
基于目录C:/test
(1)判断这个文件test.txt是否存在
import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file = new File("C:/test/test.txt"); System.out.println(file.exists()); } }
输出true;
(2) 判断test.txt是否为一个目录 / 判断test是否为一个目录
import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file1 = new File("C:/test"); File file2 = new File("C:/test/test.txt"); System.out.println(file1.isDirectory()); System.out.println(file2.isDirectory()); } }
输出:
说明C:/test/test.txt是一个文件C:/test是一个目录
(3) 在C:/test目录下没有cc空文件, 随后创建一个空目录cc
import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file1 = new File("C:/test/cc"); boolean ret = file1.createNewFile(); System.out.println(ret); } }
输出true, 然后查看test目录:
cc正确的被创建, 并返回true.
(3) 创建了cc空文件之后, 删除这个cc空文件
import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file1 = new File("C:/test/cc"); boolean ret = file1.delete(); System.out.println(ret); } }
输出true, 查看test目录, 发现cc空文件已经被删除
目录的创建与删除
(1)在C:/test目录下创建目录cam
import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file1 = new File("C:/test/cam"); boolean ret = file1.mkdir(); System.out.println(ret); } }
输出:true,
成功在Test目录下创建cam子目录
(2)在test目录中的cam目录中创建多级子目录./a/b/c
import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file1 = new File("C:/test/cam/a/b/c"); boolean ret = file1.mkdirs(); System.out.println(ret); } }
输出true:
(3) 返回Test目录下所有的文件名(除了test外, 额外添加几个文件a,b,c)
import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file1 = new File("C:/Test"); String[] retArr = file1.list(); for(String x :retArr) { System.out.println(x); } } }
输出:
通过这个我们可以发现, cam是目录, 而其他的abc和test的文本都是文件, 目录cam也被输出
(4) 将C;/Test/test.txt文件进行改名
public class Main { public static void main(String[] args) throws IOException { File file1 = new File("C:/Test/test.txt"); File file2 = new File("C:/Test/cam/test.txt"); boolean ret = file1.renameTo(file2); System.out.println(ret); } }
输出true;
Test目录中的test.txt文件被改名(绝对路径)到了Test目录中的cam目录中去了.
输入输出流
输入输出流是针对文件的内容的一种操作, 针对文本文件, 提供了一组类, 统称为字符流, 针对二进制文本文件, 提供了一组类, 统称为字节流, 字符流的基本单位为字符, 字节流的基本单位为字节.
每一个流对象, 都具有两个方向, 一个是读, 一个是写, 分别对应的Reader和InputString , Writer, outputString.
我们所说的输入输出都是针对CPU来操作的, 为了方便大家理解. 对于一组数据, 由外存或者硬盘等io设备进入CPU, 成为input, 反之, 从CPU中输出数据到主存或者是其他io设备就称为output.
下面使用的代码案例中. 其中的try with resource操作是自动执行close关闭操作的. 例如:
InputStream
方法和说明
InputStream方法概述
返回类型 | 方法签名 | 说明 |
int | read() | 返回一个字节的数据, 如果返回值为-1, 则代表读到文件末尾 |
int | read(byte[] bytes) | 最多读取bytes.length字节的数据到bytes中,返回值为读到的字节数, -1 代表读取到文件莫问 |
int | read(byte[] bytes, int offset, int len) | 最多读取到len - offset字节的数据到bytes中, 放在从offset开始, 返回值为实际读取到的字节数, -1 代表读取完了 |
void | close() | 关闭字节流 |
InputStream只是一个抽象类, 不能创建出InputStream实例来对文件进行操作, 还需要使用具体的实现类, 关于InputStream的实现类有很多, 接下来我们重点介绍他的一个实现类 FileInputStream.
FileInputStream 概述
FileInputStream有两个构造方法, 一个是FileInputStream(File file), 另一个是FileInputStream(String name), 第一个是传入一个File类实例, 第二个是直接传入一个文件路径, 构成一个字节输入流
代码实例1
存在文件C:/Test/cam/test.txt
public class IOStream { public static void main(String[] args) throws IOException { try(InputStream inputStream = new FileInputStream("C:/Test/cam/test.txt")) { while (true) { int readb = inputStream.read(); if (readb == -1) { // 代表文件已经读取完成 break; } System.out.printf("%c",readb); } } } }
代码实例2
此处的文件案例:C:/Test/cam/test.txt
public static void main(String[] args) throws IOException { try(InputStream inputStream = new FileInputStream("C:/Test/cam/test.txt")) { byte[] bytes = new byte[2]; int len; while (true) { len = inputStream.read(bytes); if (len == -1) { break; } for (int i = 0; i < len; i++) { System.out.printf("%c", bytes[i]); } } } }
字符集问题?
如果我们把下面的字符, 改成中文字符, 然后再使用InputStream的FileInputStream实现类来读取.
此处就会抛出异常, 显示IllegalFormatCodePointException
此处需要使用utf8编码来解决.
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); } } } }
此处的String有一个构造方法
Scanner 读取
从FileInputStream实现类的实例中可以看到, 我们直接对这种字符读取的话是有很多限制的, 比如字符集就是多种限制的其中之一, 他直接影响到了我们数据的读取.
为么避免这种比较麻烦的读取, 我们可以直接使用Scanner的读取方式, 我们常见的Scanner(System.in)的读取方法之外, 还有一个Scanner(InputStream is, String characterSet)
InputStream is 为输入流, characterSet为指定搞得字符集.
使用实例
还是使用上面的C:/Test/cam/test.txt文件模块, test.txt中写有你好中国:
public static void main(String[] args) throws IOException{ try(InputStream inputStream = new FileInputStream("C:/Test/cam/test.txt")) { try(Scanner scanner = new Scanner(inputStream, "UTF-8")) { while (scanner.hasNext()) { String s = scanner.next(); System.out.println(s); } } } }
OutputStream
OutputStream同样是一个抽象类, 是对比输入流InputStream的, 相对应的, OutputStream也有其对应方法, 其类型为写入, 如下:
返回类型 | 方法 | 说明 |
void | write(int b) | b为要写入的字节数据 |
void | write(byte[] bArr) | 写入字符数组bArr |
int | write(byte[] b, int offset, int len) | 从b中写入字符数组, 从offset开始, 长度为len |
void | close() | 关闭字节流 |
void | flush() | 重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为 了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的 一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写 入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的 数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush (刷新)操作,将数据刷到设备中。 |
OutputStream只是一个抽象类, 想要真正的对文件进行输出操作, 就需要一个实现类, 也就是FileOutputStream, 接下来看实例
还是以上面C:/Test/a.txt
使用实例1
public static void main(String[] args) throws IOException{ try(OutputStream outputStream = new FileOutputStream("C:/Test/a.txt")) { outputStream.write('h'); outputStream.write('e'); outputStream.write('l'); outputStream.write('l'); outputStream.write('o'); outputStream.close(); } }
在这个a.txt的空的文本文件中写入hello, 我们打开这个文件如下:
使用完之后记得使用close来关闭输入输出流
使用实例2
public static void main(String[] args) throws IOException{ try(OutputStream outputStream = new FileOutputStream("C:/Test/a.txt")) { byte[] barr = new byte[] {(byte)'h', (byte)'e',(byte)'l',(byte)'l',(byte)'o'}; outputStream.write(barr); } }
我们往已经存在数据"hello" 的文件a.txt中, 写入了单词"world", 最后的结果为world, 所以这个write为覆盖写模式.
使用实例3
往文件a.txt中写入iLoveYou中的iLve
public static void main(String[] args) throws IOException{ try(OutputStream outputStream = new FileOutputStream("C:/Test/a.txt")) { byte[] bytes = new byte[] {(byte)'i', (byte)'L',(byte)'o',(byte)'v',(byte)'e',(byte)'Y',(byte)'o',(byte)'u',}; outputStream.write(bytes,0,5); } }
使用实例4
如果我们输入的是字符串, 而不是byte[] 数组, 或者是单个字符, 那么就需要将字符串转化为byte数组, 使用String类的getBytes()方法, 如下:
public static void main(String[] args) throws IOException{ try(OutputStream outputStream = new FileOutputStream("C:/Test/a.txt")) { String a = "hello world"; byte[] bytes = a.getBytes(); outputStream.write(bytes); } }
如果这里输入的是中文字符, 需要在getBytes()方法中传入字符串"UTF-8", 但是如果编译器默认的字符集为UTF-8,则可以忽略不计:
Java输入输出流的使用案例
我们可以设计一个, 遍历目录, 来查找文件, 找出文件中包含内容为"hello"的文件绝对路径
import java.io.*; import java.util.Scanner; public class IODemo1 { public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.println("请输入您想要扫描的目录"); File file = new File(in.next()); // 如果file不是目录, 则表示输入的目录有误 if (!file.isDirectory()) { System.out.println("您输入的为非法目录"); } System.out.println("请输入您要查找的内容"); String search = in.next(); searchDir(file, search); } public static void searchDir(File file, String word) { // 1. 列出当前目录下的目录集合 File[] files = file.listFiles(); if (files == null) { // 空目录, 里面啥也没有 return; } // 遍历里面的每一个元素, 如果是文件, 就读取, 如果是目录就继续递归 for(File x : files) { System.out.println("当前搜索到" + x.getAbsolutePath()); if (x.isDirectory()) { searchDir(file,word); } else if (x.isFile()) { String content = readFile(x); if (content.contains(word)) { System.out.println(x.getAbsolutePath() + " 包含您想要的内容"); } } } } public static String readFile(File x) throws IOException { StringBuilder stringBuilder = new StringBuilder(); try (InputStream inputStream = new FileInputStream(x)) { while (true) { int b = inputStream.read(); if (b == -1) { break; } stringBuilder.append((char)b); } } return stringBuilder.toString(); } }