一:基本概念
针对硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存.这里所说的"单位"其实就是文件.
文件,是存储在硬盘上的!
Q : 硬盘和内存的区别 :
A :
- 内存存储空间小,硬盘存储空间大;
- 内存访问速度快,硬盘访问速度慢;
- 内存成本较高,硬盘比较便宜;
- 内存掉电数据丢失,硬盘掉电数据还在.
Q : 文件在计算机中的组织形式 ?
A : 文件在计算机中是按照树形结构组织的 .
Q : 如何在文件系统中定义唯一的一个文件呢 ?
A : 答案是"文件路径".
路径的分类 :
- 绝对路径 : 树中的每个结点都可以被一条从根开始,一直到达终点的路径所描述,而这种描述方式就被称为文件的绝对路径(absolute path) .以盘符开头的路径称为绝对路径 .
- 相对路径 : 除了可以从根开始进行路径的描述,我们可以从任意结点出发,进行路径的描述,而这种描述方式就被称为相对路径(relative path),相对于当前所在结点的一条路径 .以.或者…开头的路径称为相对路径 .
注意 : 目录与目录之间可以使用 / 或者 \ 进行分割 .
Q : 普通文件的分类?
A :
了解普通文件的分类 , 有助于我们后续进行文件的操作 .
Q : 其他注意事项 ?
A :
- 文件是被操作系统管理的 , 操作系统内核中有一个专门的模块 , 文件系统 , Java针对文件系统/文件进行了一系列的封装 …
- 文件由于被操作系统进行了管理,所以根据不同的用户,会赋予用户不同的对待该文件的权限,一般地可以认为有可读、可写、可执行权限。
- Windows 操作系统上 , 还有一类文件比较特殊 , 就是"快捷方式" (shortcut) , 这种文件只是对真实文件的一种引用而已 .
- 很多操作系统为了实现接口的统一性 , 将所有的 I/O 设备都抽象成了文件的概念 , 使用这一理念最为知名的就是 Unix、Linux 操作系统-----万物皆文件 .
二:文件的操作
Java 中通过 java.io.File 类来对一个文件 (包括目录) 进行抽象的描述.
对文件的操作包括 :
- 打开文件 ;
- 关闭文件 ;
- 读文件 ;
- 写文件 ;
2.1File
2.1.1构造方法
2.1.2 常用方法
2.1.2.1 常用方法介绍
2.1.2.2代码示例
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
//File file = new File("d:/test.txt");
File file = new File("./test.txt");
System.out.println(file.getParent());//父目录文件路径
System.out.println(file.getName());//纯文件名称
System.out.println(file.getPath());//文件路径
System.out.println(file.getAbsolutePath());//绝对路径
System.out.println(file.getCanonicalPath());//修饰过的绝对路径
}
}
getCanonicalPath():此路径方法返回绝对唯一的标准规范路径名。此方法首先将此路径名转换为绝对形式,就像调用getAbsolutePath方法一样,然后以系统相关的方式将其映射到其唯一路径上。
也就是说如果路径中包含“.”或“…”等当前路径及上层路径表示法,则会从路径名中删除“.”和“…”使用真实路径代替。
import java.io.File;
import java.io.IOException;
public class Demo2 {
public static void main(String[] args) throws IOException {
File file = new File("test1.txt");
System.out.println(file.exists());//是否存在
System.out.println(file.isDirectory()); //是否是一个目录
System.out.println(file.isFile());//是否是一个文件
System.out.println("=====================");
//创建文件
file.createNewFile();
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
}
}
import java.io.File;
public class Demo3 {
public static void main(String[] args) {
File file = new File("test1.txt");
System.out.println(file.exists());
file.delete();
System.out.println(file.exists());
}
}
import java.io.File;
public class Demo4 {
public static void main(String[] args) throws InterruptedException {
File file = new File("test/demo1/1.1");
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println("=============");
file.mkdirs(); //创建目录
System.out.println(file.exists());
System.out.println(file.isDirectory());
}
}
import java.io.File;
public class Demo5 {
//文件重命名
public static void main(String[] args) {
File file1 = new File("xiaoqin.txt");
File file2 = new File("xiaoxiaoqin.txt");
file1.renameTo(file2);
}
}
2.2 文件内容的读写—数据流
2.2.1 综述
在Java中关于文件读写,提供了一些类.
- 第一组 : InputStream OutputStream
- 第二组 : Reader Writer
Q : 流的分类 ?
A :
- 字节流 : 以字节为单位的流 , 用来操作二进制文件
- 字符流 : 以字符为单位的流 , 用来操作文本文件
2.2.2 InputStream
2.2.2.1 方法介绍
注意 : InputStream 不能直接实例化 , 而是需要用到子类FileInputStream , 用于读文件 .
2.2.2.2 代码示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class Demo6 {
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
try{
//1.打开文件
inputStream = new FileInputStream("xiaoxiaoqin.txt");
/** 2.读取文件---方法一
* while (true) {
* int b = inputStream.read();
* if(b == -1){
* //文件读完了
* break;
* }
* System.out.println(b);
* }
*/
/**
* 2.读取文件---方法二
* byte[] b = new byte[1024];
* int len = inputStream.read(b);
*
* for (int i = 0; i < len; i++) {
* System.out.println(b[i]);
* }
*/
// 2.读取文件---方法三
byte[] b = new byte[1024];
int len = inputStream.read(b);
String s = new String(b,0,len,"utf-8");
System.out.println(s);
} catch (IOException e){
e.printStackTrace();
} finally {
inputStream.close();
}
}
}
方法一二运行结果 :
方法三运行结果 :
2.2.2.3代码分析
注意 :
- 文件描述符表可以通过配置项 , 手动调整这里的大小 . 手动调整 , 而不是自动扩容 !
- 其次 , 如果打开后忘记关闭文件 , 其危害是非常大的 , 因为文件资源泄露是一个逐渐的过程 , 不是立即就泄露完 , 这就好像一个定时炸弹 , 当服务器程序需要7*24 h 运行时 , 指不定什么时候程序就会奔溃 !
因此 , 可以通过字符流解决上述问题 , 代码示例如下 :
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class Demo7 {
public static void main(String[] args) throws IOException {
//使用字符流读取文件
Reader reader = new FileReader("xiaoxiaoqin.txt");
char[] buffer = new char[1024];
int len = reader.read(buffer);
for (int i = 0; i < len; i++) {
System.out.print(buffer[i]);
}
reader.close();
}
}
对于文本文件 , 还有更简单的写法 , 就是借助Scanner .
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class Demo8 {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("xiaoxiaoqin.txt");
Scanner scanner = new Scanner(inputStream);
String s = scanner.next(); //读一行
System.out.println(s); //直接打印
inputStream.close();
}
}
还有一种更加简单的写法 , 无需进行try-catch-finally , 代码如下 :
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo9 {
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("xiaoxiaoqin.txt")){
byte[] b = new byte[1024];
int len = inputStream.read(b);
for (int i = 0; i < len; i++) {
System.out.println(b[i]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2.3OutputStream
2.2.3.1方法介绍
2.2.3.2代码示例
2.2.3.2.1使用字节流
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo10 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("xiaoxiaoqin.txt")){
outputStream.write('a');
outputStream.write('p');
outputStream.write('p');
outputStream.write('l');
outputStream.write('e');
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo10 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("xiaoxiaoqin.txt")){
String s = "helloworld";
outputStream.write(s.getBytes());// 可以得到一个字符数组
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意 : 每次写 , 都会把旧的内容清空掉 , 重新写入 .
2.2.3.2.2使用字符流
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Demo11 {
public static void main(String[] args) {
try(Writer writer = new FileWriter("xiaoxiaoqin.txt")){
writer.write("helloChina");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2.3.2.3使用PrintWriter类完成输出
import java.io.*;
public class Demo12 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("xiaoxiaoqin.txt")){
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println("zheshiyifenlibiexin");
printWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2.3.3程序分析
三:案例练习
3.1案例一
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件 .
import com.sun.org.apache.xerces.internal.impl.xs.SchemaNamespaceSupport;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
/***
* 扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要
* 删除该文件
*/
public class Demo13 {
//实现递归遍历文件,并询问是否要删除
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要扫描的路径:");
String rootPath = scanner.next();
File root = new File(rootPath);
if(!root.exists()){
System.out.println("路径无效!");
return;
}
System.out.println("请输入要删除的文件名(或其部分):");
String toDelete = scanner.next();
//递归查找
scanDir(root,toDelete);
}
private static void scanDir(File rootDir, String toDelete) {
File[] files = rootDir.listFiles();// 返回的是该目录下的文件和方法
if(files == null){
//空目录,直接返回
return;
}
for (File f : files) {
if(f.isDirectory()){
scanDir(f,toDelete);
} else {
tryDelete(f,toDelete);
}
}
}
/**
* 是文件,看文件名匹配吗?
* @param f
* @param toDelete
*/
private static void tryDelete(File f, String toDelete) {
if(f.getName().contains(toDelete)){
try{
System.out.println("是否要删除文件(Y/N):"+f.getCanonicalPath());
Scanner scanner = new Scanner(System.in);
String choice = scanner.next();
if(choice.equals("Y")){
f.delete();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.2案例二
进行普通文件的复制 .
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.io.*;
import java.util.Scanner;
/**
* 进行普通文件的复制
*/
public class Demo14 {
public static void main(String[] args) {
//1.找到文件
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要复制的文件路径:");
String path = scanner.next();
File file1 = new File(path);
if(!file1.exists()){
System.out.println("要复制的文件不存在!");
return;
}
if(!file1.isFile()){
System.out.println("要复制的不是普通文件!");
return;
}
System.out.println("请输入要复制到的目标路径:");
String despath = scanner.next();
File file2 = new File(despath);
if(file2.exists()){
System.out.println("要复制的目标已存在!");
return;
}
//2.进行拷贝
try(InputStream inputStream = new FileInputStream(file1)){
try(OutputStream outputStream = new FileOutputStream(file2)){
byte[] buf = new byte[1024];
while(true){
int len = inputStream.read(buf);
if(len == -1){
break;
}
outputStream.write(buf,0,len);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
程序分析 :
Q : 为什么不全部读取出来,一次性写入呢 ?
A : 其实也可以 !如果说我申请一个G的内存空间 , 比如byte[] buf = new byte[1024 * 1024 * 1024] , 那么缓冲区内容为1G了 , 大概率可以实现1次全部写入 .
Q : 在while操作时 , 不是每次都会从头开始读吗 ?
注意:buf是buffer(缓冲区)的缩写,通常是一块内存空间,在IO操作时,可以提高程序的执行效率~~
3.3案例三
遍历目录, 看某个输入的词是否在文件名或者文件内容中存在.
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
import java.util.logging.FileHandler;
/**
*遍历目录, 看某个输入的词是否在文件名或者文件内容中存在.
*/
public class Demo15 {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你要搜索的目录");
String rootPath = scanner.next();
File rootFile = new File(rootPath);
if(!rootFile.exists()){
System.out.println("要扫描的目录不存在!");
return;
}
if(!rootFile.isDirectory()){
System.out.println("要扫描的路径不是目录!");
return;
}
System.out.println("请输入要搜索的词:");
String toFind = scanner.next();
//递归遍历目录
scanDir(rootFile,toFind);
}
private static void scanDir(File rootFile, String toFind) throws IOException {
File[] files = rootFile.listFiles();
if(files == null){
return;
}
for (File f: files) {
if(f.isDirectory()){
scanDir(f,toFind);
} else {
tryFind(f,toFind);
}
}
}
//判断toFind是否是文件名或是文件内容的一部分
private static void tryFind(File f, String toFind) throws IOException {
if(f.getName().contains(toFind)){
System.out.println("找到文件名匹配的文件:"+f.getCanonicalPath());
return;
}
try(InputStream inputStream = new FileInputStream(f)){
//把文件内容整个读取出来
StringBuilder stringBuilder = new StringBuilder();
Scanner scanner = new Scanner(inputStream);
while(scanner.hasNextLine()){
stringBuilder.append(scanner.nextLine());
}
//读取完毕
if(stringBuilder.indexOf(toFind) >= 0){
System.out.println("找到文件内容匹配的文件:"+f.getCanonicalPath());
return;
}
}
}
}
以上是文件操作部分的全部内容!!!