jkd1.8后你可以试试这样的文件操作:Path&Files

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类的三种构造方式;

  1. 直接给定一个path路径:

    String path = "D:" + separator + "study" + separator + "java.txt";
    File file = new File(path);
    
  2. 使用父路径+子路径来创建

    String parent = "D://stydy//";
    String child = "java.txt";
    File file = new File(parent,child);
    
  3. 将父路径实例化一个File对象+子路径

    File parent = new File("D://stydy//");
    File file = new File(parent,"java.txt");
    
注意点

​ 创建File对象的路径不一定是要真实存在的。

File类中的方法

​ 对于File类的方法来说,可以按照我们平时创建文件时的逻辑顺序去记忆:按目录去找文件,看文件存不存在;创建文件,拷贝文件,删除文件。

  1. 获取文件的各种属性

    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();//只对文件有效
    
  2. 判断文件是否存在(创建或者读取文件时必须使用)

    File file = new File("D://stydy//java.txt");
    //判断文件或目录是否存在
    boolean b = file.exists();
    //判断是file是文件还是目录
    boolean b = file.isDirectory();
    boolean b = file.isFile();
    
  3. 文件操作

    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);
}

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值