文件输入与输出io
文件
@[toc]
狭义的文件: 存储在硬盘上的数据 , “以文件为单位”, 进行组织
常见的文件类型: 文本文件,图片 视频文件 音频文件 可执行程序
文件夹也叫做"目录"它也还是一种特殊的文件
硬盘与内存的区别
- 硬盘的存储空间比较大,内存的存储空间比较小
- 硬盘的访问速度慢,内存的访问速度快
- 硬盘的成本比较低 , 内存的成本比较高
- 硬盘的数据断电不会消失, 内存的数据断电后会消失(硬盘的持久性存储)
广义的文件
操作系统是负责管理软硬件的系统, Linux往往会把资源都抽象成"文件"
这就是"一切皆文件"
硬件也可以看做是文件
比如,一个网卡,可以把这个网卡抽象成一个文件, 创建出一个特殊的文件, 表示网卡
从网卡中接收数据就是读这个文件.
向网卡中发送数据就是写这个文件
目录结构是一个N叉树
绝对路径与相对路径
绝对路径是一个很具体的独一无二的路径
举一个例子:
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类的构造方法
主要是第二个方法,就是直接传入一个路径字符串,这个路径可以是绝对路径也可以是相对 路径
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());//获取文件的相对路径
}
}
当传的是绝对路径的时候,后面三个路径好像没有什么区别
当传入的是相对路径就会不一样了
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());//获取文件的相对路径
}
}
使用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());
}
}
删除文件
创建目录(单层 / 多层)
打印同级目录
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));
}
}
上述的操作都是在操作"文件系统",而不是真正对一个文件进行操作
针对文件的内容进行操作才是关键, 也就是读文件和写文件
文件内容的读写
Java中 将读写文件的操作比喻成"流", stream
水的特点是,流动起来,绵延不断
对应到文件的读写操作上, 要是想要写100个字节到文件中, 可以一次写1个字节,分100次写,也可以一次写10个字节,分10次写,还可以一次写20个字节,分5次写,所以这是很灵活的
Java标准库中,就在"流"的概念上,提供了一组类,来完成读写文件的操作
流的分类
字节流
读文件
InputStream有三个构造方法
一次读一个字节,读出的结果是read的返回值
这两个方法, 会把读的内容放到字节数组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码:(字节流)
写文件
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();//关闭文件
}
}
写文件
写文件的时候可以从传入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
在这个代码中,不一定能保证 一定能执行到关闭文件,万一前面抛出异常,关闭文件就执行不到了
解决方法
使用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的结果, 针对每一个元素看看它是普通文件还是子目录,要是是普通文件就选择是否删除,要是是子目录,就进行递归,继续扫描子目录的子目录
也就是说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();
}
}
}
}
练习二
实现文件的复制
实现复制比较简单,就是打开一个文件读,一个文件写
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();
}
}
}
练习三
扫描指定目录 , 并找到名称或者内容包含指定字符的所有普通文件
这个算是练习一 的进阶版,主要是增加了在文件内容中查找的程序
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);
}
}
}
}
这里的练习三代码的运行效率是很低的
- 点赞
- 收藏
- 关注作者
评论(0)