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

【JavaEE】-- 文件操作和IO

文章目录

  • 1. 认识文件
    • 1.1 树形结构组织和目录
      • 1.1.1 几个常用的命令
    • 1.2 文件路径
  • 2. Java中操作文件
    • 2.1 File概述
      • 2.1.1 属性
      • 2.1.2 构造方法
      • 2.1.3 方法
        • 2.1.3.1 观察get系列的特点和差异
        • 2.1.3.2 普通文件的创建和删除
        • 2.1.3.3
        • 2.1.3.4
        • 2.1.3.5
  • 3. 文件内容的读写--数据流
    • 3.1 InputStream概述
      • 3.1.1 方法
      • 3.1.2 说明
    • 3.2 FileInputStream概述
      • 3.2.1 构造方法
    • 3.3 代码示例
    • 3.4 OutputStream概述
      • 3.4.1 方法
      • 3.4.2 说明
      • 3.5.1 OutputStreamWriter进行字符写入
    • 3.6 输入字符流--FileReader
    • 3.7 输出字符流--FileWriter
    • 3.8 利用Scanner进行字符读取
    • 3.9 PrinterWriter
    • 3.10 处理输入输出流的关闭
  • 4. 小程序练习
    • 4.1 示例一
    • 4.2 示例二
    • 4.3 示例三

什么是IO
input和output的首字母

数据进入内存叫输入,数据从内存出去叫输出。

1. 认识文件

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

在磁盘中保存的文件,我们常用的:

  1. 自己写的文本
  2. 自己下载的程序,比如:QQ、WECHAT
  3. 系统自带的文件
  4. 特殊的文件,目录(文件夹)。
    不同的文件可以用不同的后缀名去表示(.exe文件、.mp3视频文件、.txt文件…)。

2. 广义上的文件
操作系统的主要功能就是管理计算机设备,Linux系统中所有的计算机设置都是通过文件来描述的,包括键盘、显示器、打印机、网卡…

对于系统而言,文件包括硬件和软件。进程是申请资源的最小单位,当进程需要使用某一个文件时,用一个文件描述符来表示,并加入到文件描述符表中维护起来。当进程启动时会分配三个文件描述符:标准输入(input)、标准输出(output)、标准错误(error)。

1.1 树形结构组织和目录

目录时组织普通文件的,也就是我们通常说的文件夹,目录中也可以包含子目录。

在这里插入图片描述
在这里插入图片描述

1.1.1 几个常用的命令

cd命令改变所在目录
在这里插入图片描述
dir命令显示当前目录中的内容
在这里插入图片描述

tree–只显示目录结构,不显示普通文件

在这里插入图片描述
tree /F–显示目录结构,并显示普通文件
在这里插入图片描述

1.2 文件路径

  1. D:\app-down-load\QQNT\QQ.exe这是一个完整路径,可以通过正确的完整路径找到系统中任何一个文件。
  2. 在windows中路径分隔符默认是反斜杠(\),但是在这个基础上也保留了斜杠(/),所以在windows中斜杠和反斜杠都是可以识别的,但是推荐使用/,因为使用\需要转义\。
  3. 在Linux系统中路径的分隔符默认是斜杠(/)。
  4. 当输入路径时,会先从当前目录下查找对应的文件,如果没有就会去环境变量配置中依次查找。
    在这里插入图片描述

1. 绝对路径

D:\app-down-load\QQNT\QQ.exe

从盘符或根目录开始,每个子目录用分隔符隔开,一直到目标文件。

2. 相对路径
先要确认当前的工作目录,以工作目录为基准去找目标文件。

比如,现在我所处的目录是d:\app-down-load\QQNT,想要返回上一级目录就可以输入cd ..
在这里插入图片描述
..就表示上一级目录

.表示当前目录
在这里插入图片描述
还可以直接写程序名,默认在当前目录下查找
在这里插入图片描述
在代码中访问项目中包含的文件,建议使用相对路径,因为不同机器上的工作目录可能不同。

2. Java中操作文件

2.1 File概述

文件是操作系统中的概念,Java为不同的操作系统做了封装,提供了一个File类,来让Java程序员操作文件。

2.1.1 属性

修饰符及类型属性说明
static StringpathSeparator依赖于系统的路径分隔符,String类型的表示
static CharpathSeparatorChar依赖于系统的路径分隔符,Char类型的表示
static Stringseparator目录的分隔符。String类型的表示
static CharseparatorCharChar类型的表示
public class Demo101 {public static void main(String[] args) {System.out.println(File.separator);System.out.println(File.pathSeparator);System.out.println("============");System.out.println(File.separatorChar);System.out.println(File.pathSeparatorChar);}
}

输出结果:

\
;
============
\
;

输出的\是目录的分隔符。
输出的;是多个路径之间的分隔符。windows中是;,Linux中是:

2.1.2 构造方法

文件对象,类似于Thread对象,是Java层面的 ,不是系统中的文件,只是对文件的一个描述和定义。

签名说明
File(File parent, String child)根据父目录+孩子文件路径,创建一个新的File实例
File(String pathname)根据文件路径创建一个新的File实例,路径可以是绝对路径或者相对路径
File(String parent, String child)根据父目录+孩子文件路径,创建一个新的File实例,父目录用路径表示
public class Demo02_Constructor {public static void main(String[] args) {File file = new File("d:/temp");File file1 = new File(file, "test/hello.txt");System.out.println(file);System.out.println(file1);System.out.println("===========================");File file2 = new File("d:\\temp\\test\\hello.txt");System.out.println(file2);System.out.println("===========================");File file3 = new File("d:/temp", "test/hello.txt");System.out.println(file3);}
}

输出结果:

d:\temp
d:\temp\test\hello.txt
===========================
d:\temp\test\hello.txt
===========================
d:\temp\test\hello.txt

2.1.3 方法

2.1.3.1 观察get系列的特点和差异
修饰符及返回值类型方法签名说明
StringgetParent()返回File对象的父目录文件路径
StringgetName()返回FIle对象的纯文件名称
StringgetPath()返回File对象的文件路径
StringgetAbsolutePath()返回File对象的绝对路径
StringgetCanonicalPath()返回File对象的修饰过的绝对路径
public class Demo03_get {public static void main(String[] args) throws IOException {File file = new File("d:/temp/test/hello.txt");System.out.println(file.getPath());//文件路径System.out.println(file.getParent());//父目录System.out.println(file.getName());//文件名System.out.println(file.getAbsolutePath());//绝对路径System.out.println(file.getCanonicalPath());//标准形式的路径System.out.println("=============================");File file1 = new File("./hello.txt");System.out.println(file1.getPath());//文件路径System.out.println(file1.getParent());//父目录System.out.println(file1.getName());//文件名System.out.println(file1.getAbsolutePath());//绝对路径System.out.println(file1.getCanonicalPath());//标准形式的路径}
}

运行结果:
在这里插入图片描述

2.1.3.2 普通文件的创建和删除
修饰符及返回值类型方法签名说明
booleanexists()判断File对象描述的文件是否真实存在
booleanisDirectory()判断File对象代表的文件是否是一个目录
booleanisFile()判断 File 对象代表的文件是否是-个普通文件
booleancreateNewFile()根据File 对象,自动创建一个空文件。成功创建后返回true
booleandelete()根据File对象,删除该文件。成功删除后返回true
public class Demo104_create {public static void main(String[] args) throws IOException {File file = new File("./hello.txt");System.out.println(file.exists());//falseSystem.out.println(file.createNewFile());//trueSystem.out.println(file.exists());//trueSystem.out.println(file.isDirectory());//falseSystem.out.println(file.isFile());//true}
}

在这里插入图片描述
删除file文件
在这里插入图片描述

2.1.3.3
修饰符及返回值类型方法签名说明
String[]list()返回File对象代表的目录下的所有文件名
File[]listFiles()返回File 对象代表的目录下的所有文件,以File 对象表示
public class Demo105 {public static void main(String[] args) {File file = new File("d:/temp");System.out.println(Arrays.toString(file.list()));System.out.println(Arrays.toString(file.listFiles()));}
}

输出结果:

[abc.txt, hello, Meme]
[d:\temp\abc.txt, d:\temp\hello, d:\temp\Meme]
2.1.3.4
修饰符及返回值类型方法签名说明
booleanmkdir()创建File对象代表的目录
booleanmkdirs()创建File 对象代表的目录,如果必要,会创建中间目录

在这里插入图片描述

public class Demo106 {public static void main(String[] args) {File file = new File("d:/temp/haha");File file1 = new File("d:/temp/haha1/haha11/haha111");System.out.println(file.mkdir());//trueSystem.out.println(file1.mkdir());//falseSystem.out.println(file1.mkdirs());//true}
}

在这里插入图片描述
在这里插入图片描述

2.1.3.5
修饰符及返回值类型方法签名说明
booleanrenameTo(File dest)进行文件改名,也可以视为我们平时的剪切、粘贴操作
booleancanRead()判断用户是否对文件有可读权限
booleancanWrite()判断用户是否对文件有可写权限
public class Demo107 {public static void main(String[] args) {File file = new File("d:/temp/abc.txt");File file1 = new File("d:/temp/123.txt");if (file.exists()){System.out.println(file.renameTo(file1));//true}}
}

在这里插入图片描述

读和写
在这里插入图片描述

public class Demo108 {public static void main(String[] args) {File file = new File("d:/temp/123.txt");System.out.println(file.canRead());//trueSystem.out.println(file.canWrite());//true}
}

修改读写权限:
在这里插入图片描述

public class Demo108 {public static void main(String[] args) {File file = new File("d:/temp/123.txt");System.out.println(file.canRead());//trueSystem.out.println(file.canWrite());//false}
}

3. 文件内容的读写–数据流

在这里插入图片描述
所有的文件操作都是以内存为中心的。

Java中针对输入流和输出流定义了两个抽象类(输出流–InputStream–读取操作、输出流–OutputStream–写入操作)。

在系统中有很多种文件类型,大致分为两类:1. 文本文件类型;2. 二进制文件类型。

所有的文件都可以当作二进制文件来读取,Java针对文本文件也提供了相应的类。

问题:A 、我 是不是字符?A 和 我 分别占用多少字节?
是。A占1byte;我 主要看使用了哪种字符编码集(ASCII码一个字符占用一个字节、UTF8中一个汉字占用2~4个字节)。
在这里插入图片描述

3.1 InputStream概述

3.1.1 方法

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

3.1.2 说明

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

3.2 FileInputStream概述

3.2.1 构造方法

签名说明
FileInputStream(File file)利用File构造文件输入流
FileInputStream(String name)利用文件路径构造文件输入流
public class Demo201 {public static void main(String[] args) throws FileNotFoundException {File file = new File("d:/temp/abc.txt");FileInputStream inputStream = new FileInputStream(file);FileInputStream inputStream1 = new FileInputStream("d:/temp/abc.txt");}
}

3.3 代码示例

public class Demo202_FileInputStream {public static void main(String[] args) throws IOException {FileInputStream inputStream = new FileInputStream("d:/temp/abc.txt");while (true){int read = inputStream.read();if (read == -1){break;}System.out.println(read);}}
}

输出结果:

97
98
99
13
10
230
136
145

但是由于read()方法每次读取一个字节,如果文件很大,那么就需要读取很多次,也就意味着需要大量的IO,对性能有很大的影响,为了提升效率,可以指定每次读多少个字节。

public class Demo203 {public static void main(String[] args) throws IOException {FileInputStream inputStream = new FileInputStream("d:/temp/abc.txt");byte[] bytes = new byte[1024];while (true) {//注意该代码只能放在循环中,如果放在循环外面,那么就会重复处理第一次读取的数据,//放在循环中,每次都会向后读取新的数据,所以能处理任意大的文件int len = inputStream.read(bytes);if (len == -1){break;}for (int i = 0; i < len; i++) {System.out.println(bytes[i]);}}inputStream.close();}
}

输出结果:

97
98
99
13
10
-26
-120
-111

在read()方法中对数组进行了修改,在方法外可以访问到修改过后的内容,这类参数称为输出型参数

为什么要在使用完输入流之后调用close()方法?
在创建一个文件对象时,会申请文件资源,系统中使用文件描述符表示程序使用哪个文件,再用一个文件描述符表来组织文件描述符,当打开一个文件时就会往文件描述符表中加入一个表示当前文档的元素。文件描述符表本质上是一个数组。极端情况下,文件描述符表会有内存不够用的情况,为了解决这个问题,当文件使用完之后,需要把对应的文件描述读从数组中删除。

3.4 OutputStream概述

3.4.1 方法

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

在这里插入图片描述

3.4.2 说明

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

3.5.1 OutputStreamWriter进行字符写入

说明:abc.txt文件是空白的

public class Demo204 {public static void main(String[] args) throws IOException {FileOutputStream outputStream = new FileOutputStream("d:/temp/abc.txt");outputStream.write(97);outputStream.write(98);outputStream.write(99);outputStream.write(13);outputStream.write(10);outputStream.write(230);outputStream.write(136);outputStream.write(145);outputStream.flush();outputStream.close();}
}

运行之后:
在这里插入图片描述
问:如果再次运行程序,文档中的内容是不变还是累加?
答:不变。在打开文件之后会把之前的内容全部清空,所以再次运行程序的时候,会重新将内容写进去。

3.6 输入字符流–FileReader

public class Demo205 {public static void main(String[] args) throws IOException {FileReader reader = new FileReader("d:/temp/abc.txt");while (true) {int data = reader.read();if (data == -1){break;}System.out.println(data);}reader.close();}
}

输出结果:
在这里插入图片描述

3.7 输出字符流–FileWriter

public class Demo206 {public static void main(String[] args) throws IOException {FileWriter writer = new FileWriter("d:/temp/abc.txt");writer.write('a');writer.write('b');writer.write('c');writer.write('\r');writer.write('\n');writer.write('我');writer.flush();writer.close();}
}

在这里插入图片描述

3.8 利用Scanner进行字符读取

在这里插入图片描述

public class Demo207 {public static void main(String[] args) throws IOException {FileInputStream inputStream = new FileInputStream("d:/temp/abc.txt");Scanner scanner = new Scanner(inputStream, "UTF-8");while (true) {if (!scanner.hasNext()){break;}String line = scanner.nextLine();System.out.println(line);}scanner.close();inputStream.close();}
}

输出结果:

abc
我

3.9 PrinterWriter

public class Demo208_PrinterWriter {public static void main(String[] args) throws IOException {FileOutputStream outputStream = new FileOutputStream("d:/temp/abc.txt");PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println("hello word, 你好,世界!!!");printWriter.println("嘿嘿!!!");printWriter.printf("%s:%d", "haha", 7);printWriter.flush();printWriter.close();outputStream.close();}
}

在这里插入图片描述

3.10 处理输入输出流的关闭

之前我们都是在定义输入输出流并使用完之后会单独调用close()方法来关闭流

//实现读写操作,定义输入流和输出流
FileInputStream inputStream = new FileInputStream(sourceFile);
FileOutputStream outputStream = new FileOutputStream(destFile);//关闭流
inputStream.close();
outputStream.close();

但是我们在写代码时可能会经常忘记关闭流,那么就可能会造成很大的资源损耗。
在这里插入图片描述

我们观察到InputStream这个抽象类中实现了Closeable接口,那么可以通过下面的方式来进行处理,它会自动的帮我们关闭流:

try(FileInputStream inputStream = new FileInputStream(sourceFile);FileOutputStream outputStream = new FileOutputStream(destFile);){
} catch (FileNotFoundException e) {throw new RuntimeException(e);
} catch (IOException e) {throw new RuntimeException(e);
}

我们只需要将定义输入输出流的语句写在try后面的小括号中,然后将一些使用资源的业务代码放在try的大括号中,Java会在自动执行完try块之后调用这些资源的close()方法。

4. 小程序练习

4.1 示例一

**要求:**扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件.

package lesson03;import java.io.File;
import java.io.IOException;
import java.util.Scanner;public class Demo301_FindByName {public static void main(String[] args) throws IOException {Scanner scanner = new Scanner(System.in);System.out.println("请输入要扫描的目录的路径。");String str = scanner.nextLine();File file = new File(str);if (!file.exists()){System.out.println("输入的路径不存在,请重新输入!");return;}if (!file.isDirectory()) {System.out.println("输入的路径不是一个目录,请重新输入!");return;}System.out.println("请输入要查询的字符。");String key = scanner.nextLine();if (key.isEmpty() || key == "") {System.out.println("输入的字符串不合法,请重新输入!");return;}scan(file, key);}private static void scan(File file, String key) throws IOException {File[] files = file.listFiles();if (files.length == 0){return;}for (File file1:files) {if (file1.isFile()){String name = file1.getName();if (name.contains(key)) {System.out.println("找到文件:" + file1.getCanonicalPath() + "包含关键字" + key + "是否要删除?(Y/N)");Scanner scanner = new Scanner(System.in);String s = scanner.nextLine();if (s.toLowerCase() == "y"){file1.delete();System.out.println("已删除文件:" + file1.getCanonicalPath());}}}else {//如果是目录scan(file1, key);}}}
}

以下面这个目录来对程序进行测试:
在这里插入图片描述
控制台输出结果:
在这里插入图片描述

4.2 示例二

要求: 进行普通文件的复制

看到这个需求要考虑一个问题:用字节流还是字符流?
用字节流,这样可以操作任何类型的文件。

package lesson03;import java.io.*;
import java.util.Scanner;public class Demo302_Copy {public static void main(String[] args) {System.out.println("请输入源文件的路径。");Scanner scanner = new Scanner(System.in);String source = scanner.nextLine();File sourceFile = new File(source);//判断源文件的路径是否有效if (!sourceFile.exists()){System.out.println("输入的源文件的路径不存在,请重新输入!");return;}if (!sourceFile.isFile()){System.out.println("输入的源文件不是一个普通文件,请重新输入!");return;}//判断目标文件的路径是否有效System.out.println("请输入目标路径。");String dest = scanner.nextLine();File destFile = new File(dest);if (!destFile.getParentFile().exists()){System.out.println("输入的目标文件的父目录不存在,请重新输入!");return;}if (destFile.exists()){System.out.println("输入的目标路径已存在。");}//        //实现读写操作,定义输入流和输出流
//        FileInputStream inputStream = new FileInputStream(sourceFile);
//        FileOutputStream outputStream = new FileOutputStream(destFile);
//
//        //关闭流
//        inputStream.close();
//        outputStream.close();try(InputStream inputStream = new FileInputStream(sourceFile);OutputStream outputStream = new FileOutputStream(destFile);){byte[] bytes = new byte[1024];while (true) {int len = inputStream.read(bytes);if (len == -1){break;}outputStream.write(bytes, 0, len);}outputStream.flush();} catch (IOException e) {throw new RuntimeException(e);}}
}

源文件路径:
在这里插入图片描述

目标文件的路径:
在这里插入图片描述
控制台输出结果:
在这里插入图片描述
目标文件:
在这里插入图片描述

4.3 示例三

**要求:**扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)

package lesson03;import java.io.*;
import java.util.Scanner;public class Demo303_FindByContent {public static void main(String[] args) throws IOException {//1.获取用户输入的要扫描的路径System.out.println("请输入要扫描的目录。");Scanner scanner = new Scanner(System.in);String rootPath = scanner.nextLine();File rootFile = new File(rootPath);//2. 校验路径是否符合要求if (!rootFile.exists()){System.out.println("该路径不存在,请重新输入!");return;}if (!rootFile.isDirectory()){System.out.println("该文件不是一个目录,请重新输入!");return;}//3. 用户输入关键字System.out.println("请输入要查找的关键字。");String keyword = scanner.nextLine();//4. 校验关键字if (keyword.isEmpty() || keyword == null){System.out.println("输入的关键字不能为空。");return;}//5. 循环扫描指定路径下的文件scan(rootFile, keyword);}private static void scan(File rootFile, String keyword) throws IOException {File[] files = rootFile.listFiles();if (files.length == 0){return;}for (File file1:files) {if (file1.isFile()) {boolean check = check(file1, keyword);if (check) {System.out.println("找到文件:" + file1.getCanonicalPath() + "包含关键字:"+ keyword + "是否要删除该文件?(Y/N)");Scanner scanner = new Scanner(System.in);String opt = scanner.nextLine();if (opt.toLowerCase() == "y") {file1.delete();}}else {continue;}}else {//如果是目录scan(file1, keyword);}}}private static boolean check(File file1, String keyword) throws IOException {//判断文件名if (file1.getName() == keyword) {return true;}//判断内容try(InputStream inputStream = new FileInputStream(file1)){StringBuilder stringBuilder = new StringBuilder();Scanner scanner = new Scanner(inputStream);while (true) {if (!scanner.hasNextLine()){break;}stringBuilder.append(scanner.nextLine());}if (stringBuilder.indexOf(keyword) > -1) {return true;}} catch (IOException e) {throw new RuntimeException(e);}return false;}
}

在这里插入图片描述

相关文章:

  • qml和JavaScript的QtObject是 QML 中用于存储无界面,纯数据。应用场景:计算器、遥控器、告警类型映射
  • 如何理解UDP 和 TCP 区别 应用场景
  • 前端生成UUID
  • Codeforces Round 1027 (Div. 3)
  • 学习日记-day17-5.27
  • 计算机网络练习题
  • 网络:华为S5720-52X-SI交换机重置console密码
  • 涨薪技术|0到1学会性能测试第84课-Windows Sockets数据操作
  • Nest全栈到失业(一):Nest基础知识扫盲
  • LeetCode 118 题解--杨辉三角
  • leetcode每日一题(好几天之前的) -- 3068.最大节点价值之和
  • 什么是可重组机器人?
  • 【Day38】
  • SwaggerEndPoints 配置访问外部 Swagger 文档
  • 使用蓝耘元生代 MaaS 平台 API 工作流调用技巧与实践体验
  • 九级融智台阶与五大要素协同的量子化解析
  • 仿盒马》app开发技术分享-- 确认订单页(数据展示)(端云一体)
  • 迪宇电力绝缘胶垫四大优势,用特殊橡胶配方制成,具备多项实用优势
  • Day31 -js应用 -实例:webpack jQuery的使用及其隐含的安全问题
  • MySQL 窗口函数深度解析:语法、应用场景与性能优化
  • 网站建设相关知识/脱发严重是什么原因引起的
  • 辽宁建设工程信息网企业人员调动/潍坊seo计费
  • 免费空间自助建站模板/云南网络推广
  • 合肥瑶海区教育局官网/大地seo
  • 一带一路网站建设规划书/中国企业500强最新排名
  • 乐清开发网站公司/中国没有限制的搜索引擎