java8 第六章-----文件编程

目录

6.1、File类

 6.2、Files类

6.3、IO流

6.3.1、字节流

6.3.2、字符流

6.3.3、特殊流

6.4、ByteBuffer

6.5、FileChannel


6.1、File类

描述:该类是文件和目录路径名的抽象表示。

构造方法:

创建功能:

 判断功能:

 获取功能:

 删除功能:

 6.2、Files类

jdk7 引入了 Path 和 Paths 类:

  • Path 用来表示文件路径
  • Paths 是工具类,用来获取 Path 实例
Path source = Paths.get("1.txt"); 					// 相对路径 使用 user.dir 环境变量来定位 1.txt
Path source = Paths.get("d:\\1.txt"); 				// 绝对路径 代表了 d:\1.txt
Path source = Paths.get("d:/1.txt"); 				// 绝对路径 代表了 d:\1.txt
Path projects = Paths.get("d:\\data", "projects"); 	// 绝对路径 代表了  d:\data\projects
  •  . 代表了当前路径
  •  .. 代表了上一级路径

例如:

d:
	|- data
		|- projects
			|- a
			|- b

代码:

Path path = Paths.get("d:\\data\\projects\\a\\..\\b");
System.out.println(path);				// 原来的路径
System.out.println(path.normalize()); 	// 正常化路径

输出:

d:\data\projects\a\..\b
d:\data\projects\b

检查是否存在:

Path path = Paths.get("data.txt");
System.out.println(Files.exists(path));

创建一级目录:

Path path = Paths.get("helloword");
Files.createDirectory(path);
  • 如果该目录已经存在了,会抛异常 FileAlreadyExistsException
  • 不能一次创建多级目录,否则会抛异常 NoSuchFileException

创建多级目录:

Path path = Paths.get("helloword/hello/world");
Files.createDirectories(path);

拷贝文件方法:

Path source = Paths.get("helloword/source.txt");
Path target = Paths.get("helloword/target.txt");
Files.copy(source, target);
  • 如果文件已存在,会抛异常 FileAlreadyExistsException

如果希望用 source 覆盖掉 target,需要用 StandardCopyOption 来控制

Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);

移动文件方法:

Path source = Paths.get("helloword/source.txt");
Path target = Paths.get("helloword/target.txt");
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
  • StandardCopyOption.ATOMIC_MOVE 保证文件移动的原子性

 删除文件方法:

Path target = Paths.get("helloword/target.txt");
Files.delete(target);
  • 如果文件不存在,会抛异常 NoSuchFileException

 删除为空目录:

Path target = Paths.get("helloword/hello/world");
Files.delete(target);
  • 如果目录还有内容,会抛异常 DirectoryNotEmptyException

删除非空目录:

Files.walkFileTree(Paths.get("d:\\a"), new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        Files.delete(file);
        return super.visitFile(file, attrs);
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        Files.delete(dir);
        return super.postVisitDirectory(dir, exc);
    }
});

遍历指定目录:

Files.walkFileTree(Paths.get("d:\\a"), new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        System.out.println(dir);
        return super.preVisitDirectory(dir, attrs);
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        System.out.println("\t" + file);
        return super.visitFile(file, attrs);
    }
});

拷贝多级目录:

String source = "d:\\a";
String target = "d:\\b";
Files.walk(Paths.get(source)).forEach(path -> {
    try {
        String targetName = path.toString().replace(source, target);
        // 目录文件
        if (Files.isDirectory(path)) {
            Files.createDirectory(Paths.get(targetName));
        }
        // 普通文件
        else if (Files.isRegularFile(path)) {
            Files.copy(path, Paths.get(targetName));
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
});

6.3、IO流

概述:IO流就是用来处理设备间数据传输问题的。常见的应用:文件复制、文件上传、文件下载、文件的读取、文件的写出等等。

分类:

按照数据流向来分:
	输入流:读数据
	输出流:写数据
	
按照数据类型来分:
	字节流
		字节输入流
		字节输出流
	字符流
		字符输入流
		字符输出流

注意:

  1. 如果操作的是纯文本文件,优先使用字符流
  2. 如果操作的是图片、视频、音频、应用等二进制文件,优先使用字节流
  3. 如果不确定文件类型,优先使用字节流,字节流是万能的流

6.3.1、字节流

体系:

 字节流写数据的三种方式:

 字节流读数据的三种方式:

 字节流复制文件的四种方式:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
	public static void main(String[] args) throws IOException {
		method1();
		method2();
		method3();
		method4();
	}

	// 基本字节流一次读写一个字节
	public static void method1() throws IOException {
		FileInputStream fis = new FileInputStream("sFolder\\demo.txt");
		FileOutputStream fos = new FileOutputStream("dFolder\\demo.txt");

		int by;
		while ((by = fis.read()) != -1) {
			fos.write(by);
		}

		fos.close();
		fis.close();
	}

	// 基本字节流一次读写一个字节数组
	public static void method2() throws IOException {
		FileInputStream fis = new FileInputStream("sFolder\\demo.txt");
		FileOutputStream fos = new FileOutputStream("dFolder\\demo.txt");

		byte[] bys = new byte[1024];
		int len;
		while ((len = fis.read(bys)) != -1) {
			fos.write(bys, 0, len);
		}

		fos.close();
		fis.close();
	}

	// 字节缓冲流一次读写一个字节
	public static void method3() throws IOException {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("sFolder\\demo.txt"));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("dFolder\\demo.txt"));

		int by;
		while ((by = bis.read()) != -1) {
			bos.write(by);
		}

		bos.close();
		bis.close();
	}

	// 字节缓冲流一次读写一个字节数组
	public static void method4() throws IOException {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("sFolder\\demo.txt"));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("dFolder\\demo.txt"));

		byte[] bys = new byte[1024];
		int len;
		while ((len = bis.read(bys)) != -1) {
			bos.write(bys, 0, len);
		}

		bos.close();
		bis.close();
	}
}

字节流复制单级文件夹:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
	public static void main(String[] args) throws IOException {
		File srcFolder = new File("D:\\sFolder");
		File destFolder = new File("D:\\dFolder");
		copyFolder(srcFolder, destFolder);
	}

	/**
	 * 复制单级文件夹
	 * 
	 * @param srcFolder  源文件夹
	 * @param destFolder 目的文件夹
	 * @throws IOException
	 */
	private static void copyFolder(File srcFolder, File destFolder) throws IOException {
		// 判断路径是否存在
		if (!destFolder.exists()) {
			destFolder.mkdirs();
		}
		// 获取目的文件列表
		File[] listFiles = srcFolder.listFiles();
		// 遍历目的文件列表
		for (File file : listFiles) {
			copyFile(file, new File(destFolder, file.getName()));
		}
	}

	/**
	 * 复制文件
	 * 
	 * @param srcFile  源文件
	 * @param destFile 目的文件
	 * @throws IOException
	 */
	private static void copyFile(File srcFile, File destFile) throws IOException {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
		byte[] bys = new byte[1024];
		int len;
		while ((len = bis.read(bys)) != -1) {
			bos.write(bys, 0, len);
		}
		bos.close();
		bis.close();
	}
}

字节流复制多级文件夹:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
	public static void main(String[] args) throws IOException {
		File srcFolder = new File("D:\\sFolder");
		File destFolder = new File("D:\\dFolder");
		copyFolder(srcFolder, destFolder);
	}

	/**
	 * 复制多级文件夹
	 * 
	 * @param srcFolder  源文件夹
	 * @param destFolder 目的文件夹
	 * @throws IOException
	 */
	private static void copyFolder(File srcFolder, File destFolder) throws IOException {
		// 判断路径是否存在
		if (!destFolder.exists()) {
			destFolder.mkdirs();
		}
		// 获取目的文件列表
		File[] listFiles = srcFolder.listFiles();
		// 遍历目的文件列表
		for (File file : listFiles) {
			if (file.isDirectory()) {
				copyFolder(file, new File(destFolder, file.getName()));
			} else {
				copyFile(file, new File(destFolder, file.getName()));
			}
		}
	}

	/**
	 * 复制文件
	 * 
	 * @param srcFile  源文件
	 * @param destFile 目的文件
	 * @throws IOException
	 */
	private static void copyFile(File srcFile, File destFile) throws IOException {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
		byte[] bys = new byte[1024];
		int len;
		while ((len = bis.read(bys)) != -1) {
			bos.write(bys, 0, len);
		}
		bos.close();
		bis.close();
	}
}

6.3.2、字符流

体系:

字符流写数据的五种方式:

 

 字符流读数据的四种方式:

 字符流复制文本的七种方式:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {
	public static void main(String[] args) throws IOException {
		method1();
		method2();
		method3();
		method4();
		method5();
		method6();
		method7();
	}

	// 基本字符流一次读写一个字符
	public static void method1() throws IOException {
		InputStreamReader isr = new InputStreamReader(new FileInputStream("sFolder\\demo.txt"));
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("dFolder\\demo.txt"));

		int ch;
		while ((ch = isr.read()) != -1) {
			osw.write(ch);
		}

		osw.close();
		isr.close();
	}

	// 基本字符流一次读写一个字符数组
	public static void method2() throws IOException {
		InputStreamReader isr = new InputStreamReader(new FileInputStream("sFolder\\demo.txt"));
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("dFolder\\demo.txt"));

		char[] chs = new char[1024];
		int len;
		while ((len = isr.read(chs)) != -1) {
			osw.write(chs, 0, len);
		}

		osw.close();
		isr.close();
	}

	// 文件字符流一次读写一个字符
	public static void method3() throws IOException {
		FileReader fr = new FileReader("sFolder\\demo.txt");
		FileWriter fw = new FileWriter("dFolder\\demo.txt");

		int ch;
		while ((ch = fr.read()) != -1) {
			fw.write(ch);
		}

		fw.close();
		fr.close();
	}

	// 文件字符流一次读写一个字符数组
	public static void method4() throws IOException {
		FileReader fr = new FileReader("sFolder\\demo.txt");
		FileWriter fw = new FileWriter("dFolder\\demo.txt");

		char[] chs = new char[1024];
		int len;
		while ((len = fr.read(chs)) != -1) {
			fw.write(chs, 0, len);
		}

		fw.close();
		fr.close();
	}

	// 字符缓冲流一次读写一个字符
	public static void method5() throws IOException {
		BufferedReader br = new BufferedReader(new FileReader("sFolder\\demo.txt"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("dFolder\\demo.txt"));

		int ch;
		while ((ch = br.read()) != -1) {
			bw.write(ch);
		}

		bw.close();
		br.close();
	}

	// 字符缓冲流一次读写一个字符数组
	public static void method6() throws IOException {
		BufferedReader br = new BufferedReader(new FileReader("sFolder\\demo.txt"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("dFolder\\demo.txt"));

		char[] chs = new char[1024];
		int len;
		while ((len = br.read(chs)) != -1) {
			bw.write(chs, 0, len);
		}

		bw.close();
		br.close();
	}

	// 字符缓冲流特有功能复制文本文件
	public static void method7() throws IOException {
		BufferedReader br = new BufferedReader(new FileReader("sFolder\\demo.txt"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("dFolder\\demo.txt"));

		String line;
		while ((line = br.readLine()) != null) {
			bw.write(line);
			bw.newLine();
		}

		bw.close();
		br.close();
	}
}

6.3.3、特殊流

标准输入流:

import java.io.IOException;
import java.io.InputStream;

public class Main {
	public static void main(String[] args) throws IOException {
		InputStream is = System.in;

		int by;
		while ((by = is.read()) != -1) {
			System.out.print((char) by);
		}

		is.close();
	}
}

标准输出流:

import java.io.IOException;
import java.io.PrintStream;

public class Main {
	public static void main(String[] args) throws IOException {
		PrintStream ps = System.out;

		ps.println("Hello,World");
		ps.write("Hello,World".getBytes());

		ps.close();
	}
}

字节打印流:

import java.io.IOException;
import java.io.PrintStream;

public class Main {
	public static void main(String[] args) throws IOException {
		PrintStream ps = new PrintStream("ps.txt");

		ps.println(97);
		ps.write(97);

		ps.close();
	}
}

字符打印流:

import java.io.IOException;
import java.io.PrintWriter;

public class Main {
	public static void main(String[] args) throws IOException {
		PrintWriter pw = new PrintWriter("pw.txt");

		pw.println("hello");
		pw.write("Hello");

		pw.close();
	}
}

对象序列化流:

注意:需要实现Serializable接口,同时需要给出serialVersionUID

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Student implements Serializable {
	private static final long serialVersionUID = 5923003911550370832L;
	private String name;
	private Integer age;

	public Student() {
		super();
	}

	public Student(String name, Integer age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
}

public class Main {
	public static void main(String[] args) throws IOException {
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt"));

		Student s = new Student("曹晨磊", 30);
		oos.writeObject(s);

		oos.close();
	}
}

对象反序列化流:

注意:成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

class Student implements Serializable {
	private static final long serialVersionUID = 5923003911550370832L;
	private String name;
	private Integer age;

	public Student() {
		super();
	}

	public Student(String name, Integer age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
}

public class Main {
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt"));

		Object obj = ois.readObject();
		Student s = (Student) obj;
		System.out.println(s);

		ois.close();
	}
}

6.4、ByteBuffer


使用步骤:

向 buffer 写入数据,例如调用 channel.read(buffer)
调用 flip() 切换至读模式
从 buffer 读取数据,例如调用 buffer.get()
调用 clear() 或 compact() 切换至写模式
重复 1~4 步骤
代码演示:

try (FileChannel channel = new FileInputStream("data.txt").getChannel()) {
    ByteBuffer buffer = ByteBuffer.allocate(10);
    while (true) {
        int len = channel.read(buffer);
        if (len == -1) break;
        buffer.flip();
        while (buffer.hasRemaining()) {
            byte b = buffer.get();
            System.out.print((char) b);
        }
        buffer.clear();
    }
} catch (IOException e) {
    e.printStackTrace();
}

运行效果:

Hello,World

内部结构:

ByteBuffer 有以下重要属性:

  • capacity
  • position
  • limit

一开始:

 写模式下,position 是写入位置,limit 等于容量,下图表示写入了 4 个字节后的状态。

flip 动作发生后,position 切换为读取位置,limit 切换为读取限制。

 读取 4 个字节后,状态:

 clear 动作发生后,状态:

 compact 方法,是把未读完的部分向前压缩,然后切换至写模式。

 

分配空间:

可以使用 allocate 方法为 ByteBuffer 分配空间,其它 buffer 类也有该方法。

Bytebuffer buf = ByteBuffer.allocate(16);
  • ByteBuffer.allocate(16).getClass():class java.nio.HeapByteBuffer,java 堆内存,读写效率低,受到 GC 的影响,分配的效率高
  • ByteBuffer.allocateDirect(16).getClass():class java.nio.DirectByteBuffer,直接内存,读写效率高,不受 GC 影响,分配的效率低

 

向 buffer 写入数据:

有两种办法:

  • 调用 channel 的 read 方法
int readBytes = channel.read(buf);
  • 调用 buffer 自己的 put 方法
buf.put((byte)127);

从 buffer 读取数据:

有两种办法:

  • 调用 channel 的 write 方法
int writeBytes = channel.write(buf);
  • 调用 buffer 自己的 get 方法
byte b = buf.get();

get 方法会让 position 读指针向后走,如果想重复读取数据

  • 可以调用 rewind 方法将 position 重新置为 0
  • 或者调用 get(int i) 方法获取索引 i 的内容,它不会移动读指针

mark 和 reset方法:

mark 是在读取时,做一个标记,即使 position 改变,只要调用 reset 就能回到 mark 的位置,但是,rewind 和 flip 都会清除 mark 。

字符串与 ByteBuffer 互转:

第一种方式:

ByteBuffer buffer = StandardCharsets.UTF_8.encode("abc123");
String string = StandardCharsets.UTF_8.decode(buffer).toString();
System.out.println(string);

第二种方式:

ByteBuffer buffer = Charset.forName("utf-8").encode("abc123");
String string = Charset.forName("utf-8").decode(buffer).toString();
System.out.println(string);

6.5、FileChannel


Channel 介绍:

Channel 有一点类似于 Stream,它就是读写数据的双向通道,可以从 Channel 将数据读入 buffer,也可以将 buffer 的数据写入 Channel,而之前的 Stream 要么是输入,要么是输出,Channel 比 Stream 相比更为底层。

Channel 分类:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

FileChannel 获取方法:

FileChannel 只能工作在阻塞模式下,不能直接打开 FileChannel,必须通过 FileInputStream、FileOutputStream 或者 RandomAccessFile 来获取 FileChannel,它们都有 getChannel 方法。

  • 通过 FileInputStream 获取的 channel 只能读
  • 通过 FileOutputStream 获取的 channel 只能写
  • 通过 RandomAccessFile 是否能读写根据构造 RandomAccessFile 时的读写模式决定

 

FileChannel 读取方法:

会从 channel 读取数据填充 ByteBuffer,返回值表示读到了多少字节,-1 表示到达了文件的末尾。

int readBytes = channel.read(buffer);

FileChannel 写入方法:

写入的正确姿势如下,在 while 中调用 channel.write 是因为 write 方法并不能保证一次将 buffer 中的内容全部写入 channel。

ByteBuffer buffer = ...;
buffer.put(...); // 存入数据
buffer.flip();   // 切换读模式

while(buffer.hasRemaining()) {
    channel.write(buffer);
}

FileChannel 关闭方法:

channel 必须关闭,不过调用了 FileInputStream、FileOutputStream 或者 RandomAccessFile 的 close 方法会间接地调用 channel 的 close 方法,或者使用 try 代码块自动释放。
 

try (FileChannel channel = new FileInputStream("data.txt").getChannel()) {

} catch (IOException e) {
    e.printStackTrace();
}

FileChannel 位置方法:

获取当前位置

long pos = channel.position();

设置当前位置

long newPos = ...;
channel.position(newPos);

设置当前位置时,如果设置为文件的末尾,这时读取会返回 -1 ,这时写入,会追加内容,但要注意,如果 position 超过了文件末尾,再写入时在新内容和原末尾之间会有空洞(00)。

FileChannel 大小方法:

使用 size 方法获取文件的大小。

FileChannel 强制写入:

操作系统出于性能考虑,会将数据缓存,不是立刻写入磁盘。可以调用 force(true) 方法将文件内容和元数据(文件权限等信息)立刻写入磁盘。

FileChannel 文件拷贝:

try (
    FileChannel from = new FileInputStream("data.txt").getChannel();
    FileChannel to = new FileOutputStream("to.txt").getChannel();
) {
    long size = from.size();
    // remain 变量代表还剩余多少字节
    for (long remain = size; remain > 0; ) {
        remain -= from.transferTo((size - remain), remain, to);
    }
} catch (IOException e) {
    e.printStackTrace();
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值