java中的文件操作类
一、File类
什么是File类?
要知道一个事物是用来做什么的,我们就要知道对于这个事物的定义是什么!所以我们有必要了解在java中对于File类的定义。
java把计算机的文件(file)和目录(direcory)名抽象为了一个File类,并给我们提供了日常我们对文件和目录的创建,复制和删除等操作。File类是文件和目录 名 的抽象,所以和操作系统无关,任何操作系统都可以使用这个类中的方法。注意粗体文字 名
创建File对象
创建一个File对象也很简单,只要提供一个String类型的 路径(绝对路径或者相对路径)。但是一旦涉及到路径问题,必然要考虑的就是 “分隔符” 问题。
//不同操作系统下分隔符的类型
windows:路径分隔符 ;
文件分隔符 \
//例如我要找d盘下,study目录下的java.txt文件
d:\study\javatxt
Linux:路径分隔符 :
文件分隔符 /
//在Linux系统的opt目录下的java.txt
/opt/java.txt
那我我们该怎么去适配所有的操作系统呢
File类给我们提供了以下方法来获取文件分隔符和路径分隔符
String separator = File.separator;//separator就可以代替为本操作系统的文件分隔符
String pathSeparator = File.pathSeparator;//pathSeparator就可以代替为本操作系统的路径分隔符
之后我们就可以用如下方法来代替路径了
String path = "D:" + separator + "study" + separator + "java.txt";
对于相对路径和绝对路径就不在赘述了,只要知道绝对路径是从头到尾;相对路径就是相对于项目的根目录。
以下是File类的三种构造方式;
-
直接给定一个path路径:
String path = "D:" + separator + "study" + separator + "java.txt"; File file = new File(path);
-
使用父路径+子路径来创建
String parent = "D://stydy//"; String child = "java.txt"; File file = new File(parent,child);
-
将父路径实例化一个File对象+子路径
File parent = new File("D://stydy//"); File file = new File(parent,"java.txt");
注意点
创建File对象的路径不一定是要真实存在的。
File类中的方法
对于File类的方法来说,可以按照我们平时创建文件时的逻辑顺序去记忆:按目录去找文件,看文件存不存在;创建文件,拷贝文件,删除文件。
-
获取文件的各种属性
File file = new File("D://stydy//java.txt"); //获取文件的所在的绝对路径 String absolutePath = file.getAbsolutePath(); //将次File对象转换为路径名字符床 String path = file.getPath(); //获取文件名或者目录名 String fileName = file.getName(); //获取文件的大小 Long size = file.length();//只对文件有效
-
判断文件是否存在(创建或者读取文件时必须使用)
File file = new File("D://stydy//java.txt"); //判断文件或目录是否存在 boolean b = file.exists(); //判断是file是文件还是目录 boolean b = file.isDirectory(); boolean b = file.isFile();
-
文件操作
File file = new File("D://stydy//java.txt"); //创建一个单级目录 boolean b = file.mkdir();//成功创建返回true,其他返回false //创建一个多级目录或者单级目录和Linux命令就很像, boolean b = file.mkdirs();//成功创建返回true,其他返回false //创建一个新的空文件 boolean b = file.createNewFile();//成功创建返回true,其他返回false //删除一个file表示的文件或者目录(直接将文件从磁盘删除,谨慎操作),但是目录中有内容就会返回false boolean b = file.delete();//成功创建返回true,其他返回false
这边先不说如何去遍历用户的文件系统,因为在后面的jdk1.7之后在java.nio包小又加入了Files和Path。用来提供更好的遍历需求
二、操作文件(Path & Files)
按照java核心技术中的描述:Path和Filles类封装了用户机器上处理文件系统的所需的所有功能。我们之前从计算机磁盘中读取文件,我们并不关心怎么去操作文件,虽然我们也使用了File类完成了创建文件等操作。但不得不说,当时我们关心的还是怎么去读取文件的内容,而不是怎么去操作文件本身。
对于在程序中操作文件,我最容易想到的就是每次打开IDEA,创建项目时会让我们选择在哪个文件夹创建项目。在哪个选择窗口你既可以创建一个新的目录来创建项目,也可以使用已经存在的目录下来创建项目。在我看来这就是最纯粹的操作一个计算机的文件系统。
那么IDEA是怎么知道我某个磁盘下有哪些文件的呢?他执行了哪些程序去完成这些需求的?
这就涉及到了java的文件操作,Path类和Files提供了相比于File类更加便捷的文件操作。
使用Path类搞定路径问题
Path表示的是一个目录名序列,后面可以跟一个文件名。创建一个Path实例需要使用另一个Paths类的get方法。
Path absolute = Paths.get("D:","study","java.txt");//创建一个绝对路径
Path relative = Paths.get("java.txt");//从项目根路径创建一个相对路径
这里的路径和File一样,不必是真实存在的,仅仅是一个抽象的名字序列罢了。
组合和解析路径
在文件操作时,组合和解析路径是经常使用的操作。那么该如何使用Path来完成这些操作呢?
首先我们先定义一个基路径:
Path basePath = Paths.get("D:","study");
之后就是我们想要查找一个目录java,这时我们就像创建一个路径 path
Path path = Paths.get("java");
使用Path的 resolve() 方法
Path newPath = basePath.resolve(path);
//这时如果path是一个相对路径,newPath 的名字序列就是 :D://study//java
//如果path是一个绝对路径,那么 newPath 的名字序列就是:path 的 名字序列
一般来说我们就是想在基路径上创建一个新的子目录,java就给我们提供了 resolve的 简便使用方法
Path newPath = basePath.resolve("java");// D://study//java
//只接收一个字符串在基路径下创建一个新的子目录。
那如果想在当前目录 D://study//java 下创建一个目录和java是同级的目录呢
Path curentPath = Paths.get("D:","study","java");
Path newPath = curentPath.resolveSibling("jky");//D://study//jky
接下来,如果我知道了一个absolutePath 是指 一个绝对路径,另一个basePath是一个基路径,想要获得这个absolutePath 相对于 basePath 的一个 相对路径
Path relativePath = absolutePath.relativize(basePath);
最后就是将path路径分割的方法了
//首先我们想创建一个绝对路径
Path absolute = Paths.get("D:","study","java.txt");
//获得该路径下的文件
Path file = absolute.gitFileName();//java.txt
//获取该路径文件的父目录
Path dir = absolute.getParent();// D:\\study\\
//获取该路径的根目录
Path root = absolute.getRoot();// D:
可以使用直接使用path在控制台打印
public static void main(String[] args) {
Path absolutePath = Paths.get("D:","study","java.txt");
Path relativePath = Paths.get("java.txt");
System.out.println(absolutePath);
System.out.println(relativePath);
}
//打印结果
D:\study\java.txt
java.txt
和前辈File的联动
Path path = file.toPath();
File file = path.toFile();
使用Files类搞定文件读写和创建问题
Files搞定文件读写
在没有Files类之前,我们想要读取文件的内容或者是向文件中写入数据,依靠的就是IO操作。那么有了Files类之后,我们就可以一种更加方便的方式去 读写 中等长度的 文件内容。注意是中等长度,要是文件太大还是建议使用IO流去操作。
先设定一个情景:我有一个Path对象 path,他包含了一个文件的路径;现在我想读取这个文件中的内容
Path path = Paths.get("D:","study","java.txt");
我想一次性全部读完
byte[] data = Files.readAllBytes(path);
我想将读到的data按照字符编码的方式生成一个String
String str = new String(data,charset);
我想按行读取
List<String> lines = Files.readAllLines(path,charset);
我想写出一个字符串content到文件中的末尾
Files.write(path,content.getBytes(charset),StandardOpenOption.APPEND);
我想将之前的集合lines 写入到文件中
Files.write(path,lines);
代码示例
package com.files_path_files.files_demo;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;
/**
* 用于测试Files类的一些功能
*
* @auther Stiles-JKY
* @date 2020/3/10-23:11
*/
public class FilesApplication {
public static void main(String[] args) {
//利用之前Path中创建文件java.txt来演示操作
Path path = Paths.get("D:", "study", "java.txt");
//首先向java.txt中写入一些内容
String content = "this is java.txt who want read me";
if (Files.exists(path)) {
try {
Files.write(path, content.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
}
//从文件中读取内容
if (Files.exists(path)) {
try {
byte[] data = Files.readAllBytes(path);
String str = new String(data, StandardCharsets.UTF_8);
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
}
}
//按行读取,并按行写入
List<String> line = null;
if (Files.exists(path)) {
try {
//按行读取
line = Files.readAllLines(path, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(line);
//按行写入
try {
Files.write(path,line,StandardCharsets.UTF_8,StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Files搞定文件的创建和目录
创建一个新目录
//如果要创建的新目录前面的路径已经存在
Files.createDirectory(path);
//如果创建的新目录前面的路径未存在
Files.createDirectories(path);
在目录下创建一个新的文件
File.createFile(path);
//代码示例
public static void main(String[] args) {
Path absolutePath = Paths.get("D:","study","java.txt");
Path relativePath = Paths.get("java.txt");
System.out.println(absolutePath);
System.out.println(relativePath);
System.out.println(absolutePath.getFileName());
//使用Path和Files创建文件
if (!Files.exists(absolutePath)) {
try {
Files.createDirectories(absolutePath.getParent());
Files.createFile(absolutePath);
} catch (IOException e) {
e.printStackTrace();
}
}
if (!Files.exists(relativePath)) {
try {
Files.createFile(relativePath);
//运行之后你会发现自己的项目列表里面出现了java.txt
} catch (IOException e) {
e.printStackTrace();
}
}
}
关于文件的创建有以下需要注意的点:
首先如果要创建的文件已经存在,则抛出异常。所以我们必须先去检查文件是否存在Files.exists(path);
再者其他程序无法在此过程中执行文件创建
有时候我们想创建一个临时文件,就可以使用以下方法
Path dir = Paths.get("");
String prefix = "XXX";
String surfix = "YYY";
Path newPath = Files.createTempFile(dir,prefix,surfix);// dir\prefix.surfix
//prefix和surfix都可以为null。
Path newPath = Files.createTempFile(dir,prefix);
Path newPath = Files.createTempFile(dir,surfix);
Path newPath = Files.createTempFile(dir);
Files搞定文件的复制,移动和删除
要移动或者复制一个文件,首先我们得知道这个文件所在的路径,以及文件的目标路径。这里我们先设定文件所在路径为sourcePath;文件目标路径为targetPath
Path sourcePath = Paths.get("");
Path targetPath = Paths.get("");
之后就可以使用Files的移动和拷贝方法来实现文件的移动和复制
//文件的拷贝
Files.copy(sourcePath,targetPath);
//文件的移动
Files.move(sourcePath,targetPath);
注意:以上写法在目标路径有同名文件时移动和复制会失败,那么如果我们想覆盖源文件,想要复制所有的文件属性,或者想将移动操作定义为原子性(除了移动成功后文件位置改变外,只要失败文件在原路径保持不变)。
//文件的拷贝
Files.copy(sourcePath,targetPath,
StandardCopyOption.REPLACE_EXISTING,//覆盖源文件
StandardCopyOption.COPY_ATTRIBUTES);//复制所有的文件属性
//文件的移动
Files.move(sourcePath,targetPath,StandardCopyOption.ATOMIC_MOVE);
代码示例:
/**
* 用来演示Files的文件操作
*
* @auther Stiles-JKY
* @date 2020/3/10-23:54
*/
public class FilesApplication2 {
public static void main(String[] args) {
Path sourcePath = Paths.get("D:", "study", "java.txt");
Path targetPath1 = Paths.get("F:", "usejky","java.txt");
Path targetPath2 = Paths.get("E:", "usejky","java.txt");
//文件的拷贝
if (Files.exists(sourcePath)) {
try {
Files.copy(sourcePath, targetPath1,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES);
System.out.println("执行了拷贝");
} catch (IOException e) {
e.printStackTrace();
}
}//拷贝时不需要创建目标路径
//文件的移动
if (Files.exists(sourcePath)) {
try {
Files.createDirectories(targetPath2.getParent());
Files.move(sourcePath,targetPath2,
StandardCopyOption.REPLACE_EXISTING);
System.out.println("执行了移动");
} catch (IOException e) {
e.printStackTrace();
}
}//移动时一定要想创建目标路径,否则会报错
}
}
利用Files类,我们甚至可以将一个输入流复制到Path中,或者将一个Path复制到一个输出流中。
Files.copy(InputStream,targetPath);
Files.copy(sourcePath,OutputStream);
删除文件
//一般删除文件就是
Files.delete(path);
删除文件可以判断下该文件是否存在,防止出现异常
Files.deleteIfExists(path);
//代码示例
Path delPath = Paths.get("E:", "usejky","java.txt");
//文件的删除
try {
Files.deleteIfExists(delPath);
System.out.println("this file is already delete");
} catch (IOException e) {
e.printStackTrace();
}
读取文件的信息
文件的信息一般包含以下几种:
- 文件是否存在
- 文件是否隐藏
- 文件读写权限
- 文件大小
- 文件拥有者
- 等等
Files.exists(path);//查看文件是否存在
Files.isHidden(path);//查看文件是否隐藏
Files.isReadable(path);//文件是否可读
Files.isWritable(path);//文件是否可写
Files.isRegularFile(path);
Files.isDirectory(path);//是否为目录
Files.isSymbolicLink(path);
long size = Files.size(path);//文件大小
Files.getOwner(path);//文件拥有者
BasicFileAttributes bfa = Files.readAttributes(path,BasicFileAttributes.class);//获取文件属性
使用Files访问目录
使用Files来访问目录需要明确自己想要访问本目录还是本目录及其子目录
如果单单是为了访问本目录,只要使用Files.List(path)这个静态方法就可以了。
//以下是为了得到本目录下的所有项
Path dir = Paths.get("D:","study");//该目录下有文件:1.txt 2.txt 目录:next
//只访问本目录中的项
try (Stream<Path> entries = Files.list(dir)){
entries.forEach(dirs -> System.out.println(dirs));
} catch (Exception e) {
e.printStackTrace();
}
//输出结果
D:\study\1.txt
D:\study\2.txt
D:\study\next
当你想要处理目录中的所有子目录时,就可以使用Files.walk(path,depth),方法
//访问该目录下所有的子目录
try (final Stream<Path> all = Files.walk(dir)){
all.forEach(path -> System.out.println(path));
} catch (IOException e) {
e.printStackTrace();
}
D:\study
D:\study\1.txt
D:\study\2.txt
D:\study\next
D:\study\next\3.txt
使用流的方式来访问给定目录下所有子目录
try (final Stream<Path> all = Files.walk(dir)){
all.forEach(path -> System.out.println(path));
} catch (IOException e) {
e.printStackTrace();
}
使用目录流的方式来打印给定目录下的所有子目录
//打印目录流下的所有子目录
Files.walkFileTree(Paths.get("f:"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
System.out.println(dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException {
return FileVisitResult.SKIP_SUBTREE;
}
});
//使用目录流删除目录中的所有子目录和文件
//使用目录流删除目录中的所有子目录和文件
Path root = Paths.get("F:","score");
System.out.println(root);
Files.walkFileTree(root,new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Files.deleteIfExists(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException {
if (exc != null) throw exc;
Files.deleteIfExists(dir);
return FileVisitResult.CONTINUE;
}
});
文件系统:ZIP文件系统
使用FileSystem fs = new FileSystem(Paths.get(zipName),null);可以建立一个文件系统,依靠这个文件系统就可以操作zip文件系统。
内存映射文件
对应于使用大型文件的读取,比随机读取有更高效的方法,就是内存映射文件的使用。使用方式可以利用FileChannel 类 从文件中获取通道channel(用于磁盘文件的一种抽象);再利用通道的Map方法向内存中申请一块缓冲区用于存放映射文件数据。
一定要注意你用的是什么凡是获取的 channel
如果是直接通过路径获取的通道,那么缓冲区只是read_only。
如果是使用IO流获取的channel那么要注意了你是输入,输出还是随机访问。具体内容在FileChannel来详细解释。
public class ApplicationMemory {
/**
* 使用步骤:
* 从文件中获取一个通道
* 然后通过map方法从通道中向内存中申请一个缓冲区ByteBuffer
*/
private static Path filePath = Paths.get("f:", "FileReview", "a.txt");
public static void main(String[] args) {
//调用getFileChannel方法打开文件通道
FileChannel channel = getFileChannel(filePath);
int length;
//获取文件大小
try {
length = (int) channel.size();
//通过文件通道映射文件
MappedByteBuffer mapBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
byte[] bytes = new byte[length];
//通过mapBuffer读取文件
for (int i = 0; i < mapBuffer.limit(); i++) {
bytes[i] = mapBuffer.get(i);
}
//将bytes数组转换为String数组
String str = bytesToString(bytes);
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
}
}
public static FileChannel getFileChannel(Path path) {
//检测文件是否存在
if (!Files.exists(path)) {
throw new RuntimeException();
}
//获取文件通道
FileChannel channel = null;
try {
channel = FileChannel.open(path);
} catch (IOException e) {
e.printStackTrace();
}
return channel;
}
public static String bytesToString(byte[] bytes) {
return new String(bytes);
}
}