HacKerQWQ的博客空间

Java IO/NIO读写文件

Word count: 1.8kReading time: 8 min
2021/07/28 Share

文件的编码

  • gbk编码的中文占2个字节,英文占1个字节
  • utf-8编码的中文占3个字节,英文占1个字节
  • utf-16be编码为双字节编码

项目默认使用的编码根据IDEA设置的编码决定

image-20210728134838983

  • 编码和解码的字符集要相同,否则会出现乱码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.test.FileIO;

import java.nio.charset.StandardCharsets;

public class FileDemo {


public static void main(String[] args) throws Exception{
String s = "中国";
//gbk编码
byte[] bytes1 = s.getBytes("gbk");
System.out.print("gbk编码:");
for (byte b:bytes1){
//异或0xff是为了去除前面24个字节的00
System.out.print(Integer.toHexString(b&0xff)+" ");
}
System.out.println("");
//utf-8编码
byte[] bytes2 = s.getBytes(StandardCharsets.UTF_8);
System.out.print("utf-8编码:");
for (byte b:bytes2){
System.out.print(Integer.toHexString(b&0xff)+" ");
}
System.out.println("");
//utf-16be编码,
byte[] bytes3 = s.getBytes(StandardCharsets.UTF_16BE);
System.out.print("utf-16be编码: ");
for (byte b:bytes3){
System.out.print(Integer.toHexString(b&0xff)+" ");
}
System.out.println("");
//从utf-16be字节序列中创建gbk字符序列,显示乱码
String str1 = new String(bytes3,"gbk");
System.out.println(str1);
//从utf-16be字节序列中创建utf16-be字符序列,显示正常
String str2 = new String(bytes3, StandardCharsets.UTF_16BE);
System.out.println(str2);

}
}

运行结果:

image-20210728135458402

File类常用API

  • File类只用于表示文件或者目录,不用于文件或目录的读写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#表示文件
File file = new File("src/test.txt")
#文件分隔符,用于适应不同系统
File.separator
#判断文件或目录是否存在
file.exists()
#删除文件或目录
file.delete()
#判断文件类型
file.isDirectory();
file.isFile();
#列出当前目录下的文件的字符串形式
file.list()
#列出当前目录下的文件的File类的对象数组
file.listFiles()

遍历目录的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class FileList {
public static void listDirectory(File dir){
if (!dir.exists()){
System.out.println("目录"+dir+"不存在");
}
if (dir.isFile()){
throw new IllegalArgumentException("传入的不是目录");
}
File[] files = dir.listFiles();
for (File file:files){
// System.out.println(file);
if (file.isDirectory()){
listDirectory(file);
}else{
System.out.println(file);
}
}
}
}

IO/NIO

Java对文件的读写分为阻塞模式的IO和非阻塞的NIO

RandomAcessFile

RandomAcessFile类使用模式来控制是读还是写,有以下四种模式

  • r(只读)、rw(读写)、
  • rws(读写内容同步)
  • rwd(读写内容或元数据同步)

读文件这里使用ByteArrayOutputStream输出文件内容,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class RandomFile {
public static void main(String[] args) throws Exception{
//创建File对象demo
File demo = new File("src/flag.txt");
//创建RandomAccessFile对象
RandomAccessFile raf = new RandomAccessFile(demo,"rw");
//定义每次读取的字节数
byte[] b = new byte[1024];
//用于判断是否读写成功
int a = -1;
//创建二进制输出流对象
ByteArrayOutputStream out = new ByteArrayOutputStream();
//循环读取
while((a=raf.read(b))!=-1){
out.write(b,0,a);
}
//输出
System.out.println(out.toString());
//关闭文件
raf.close();
}
}

RandomAccessFile写文件

1
2
3
4
RandomAccessFile demo1 = new RandomAccessFile("src/flag.txt","rw");
String content = "flag is stolen by me";
demo1.write(content.getBytes(StandardCharsets.UTF_8));
demo1.close();

FileInputStream&&FileOutputStream

FileInputStreamFileOutputStream都是采用字节流操作文件文件

FileInputStream读取文件

RandomAccessFile读取文件大同小异

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class InputStreamTest {
public static void main(String[] args) throws IOException {
//创建FileInputStream对象
FileInputStream fileInputStream = new FileInputStream("src/flag.txt");
byte[] bytes = new byte[1024];
int a = -1;
ByteArrayOutputStream out = new ByteArrayOutputStream();
//循环读出文件内容
while((a=fileInputStream.read(bytes))!=-1){
out.write(bytes,0,a);
}
System.out.println(out);
}
}

FileOutputStream写入文件

1
2
3
4
5
6
7
8
9
10
11
12
public class OutputStreamTest {
public static void main(String[] args) throws IOException {
//创建FileOutStream对象
FileOutputStream fileOutputStream = new FileOutputStream(new File("src/flag.txt"));
String content = "file was modified by me--Hack";
//以字节形式输入字节流
fileOutputStream.write(content.getBytes(StandardCharsets.UTF_8));
//将缓存的数据发出去
fileOutputStream.flush();
fileOutputStream.close();
}
}
  • fileOutputStream实现了缓存机制,write()的时候不一定能写入到文件,使用flush()强制性地将缓存区的内容输出到文件

BufferedInputStream&&BufferedOutputStream

BufferedInputStream&&BufferedOutputStream采用缓冲区读取文件内容和写入文件内容,效率会比FileInputStreamFileOutputStream高一些,Buffered相当于用水桶运水,FileInput相当于一滴一滴运水

BufferedInputStream读取文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BufferStream {
public static void main(String[] args) throws Exception{
//创建BufferedInputStream对象,以FileInputStream对线为参数
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("src/flag.txt"));
//输出文件内容
int a = -1;
byte[] b = new byte[1024];
ByteArrayOutputStream out = new ByteArrayOutputStream();
while ((a=bufferedInputStream.read(b))!=-1){
out.write(b,0,a);
}
System.out.println(out.toString());
}
}

BufferedOutputStream写入文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BufferStream {
public static void main(String[] args) throws Exception{
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("src/flag.txt"));
//要写入的字符串
String content = "Modified by me again:)";
//写入字符串的字节流
bufferedOutputStream.write(content.getBytes(StandardCharsets.UTF_8));
//将缓冲区的内容强制发送到文件
bufferedOutputStream.flush();
//关闭文件
bufferedOutputStream.close();

}
}

InputStreamReader&&OutputStreamWriter

  • InputStreamReader完成byte流解析为char流,按照编码解析
  • OutputStreamWriter提供char流到byte流,按照编码处理

InputStreamReader读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
public class InputReaderTest {
public static void main(String[] args) throws Exception{
//创建InputStreamReader实现从byte流到char流的编码解析,此处指定编码为UTF-8
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("src/flag.txt"), StandardCharsets.UTF_8);
char[] strings = new char[1024];
int a = -1;
StringBuilder stringBuilder = new StringBuilder();
while((a=inputStreamReader.read(strings))!=-1){
stringBuilder.append(strings,0,a);
}
System.out.println(stringBuilder);
}
}

OutputStreamWriter写入文件

1
2
3
4
5
6
7
8
9
public class OutputWriter {
public static void main(String[] args) throws Exception{
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("src/flag.txt"));
String content = "this is writen by OutputStreamWriter!";
outputStreamWriter.write(content);
outputStreamWriter.flush();
outputStreamWriter.close();
}
}

同样flush()用于强制写入文件

BufferedReader&BufferedWriter&PrintWriter

BufferedReader&BufferedWriter特点是

  • 读取字符流
  • 支持读取/写入一行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class BuffTest {
public static void main(String[] args) throws Exception {
//创建BufferReader对象,由FileInputStream=>InputStreamReader=>BufferedReader
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("src/flag.txt")));
//创建BufferedWriter对象,由FileOutputStream=>OutputStreamReader=>BufferedReader
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("src/test.txt")));
String line = "";
//逐行读取flag.txt的内容再逐行写入test.txt
while ((line=bufferedReader.readLine())!=null){
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
//关闭Reader和Writer
bufferedReader.close();
bufferedWriter.close();

}
}
  • 注意:通过BufferedWriter写入的时候不会自动换行,需要使用newLine()函数换行

通过PrintWriter写入文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BuffTest {
public static void main(String[] args) throws Exception {
//创建BufferReader对象,由FileInputStream=>InputStreamReader=>BufferedReader
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("src/flag.txt")));
PrintWriter printWriter = new PrintWriter("src/test.txt");
String line = "";
//逐行读取flag.txt的内容再逐行写入test.txt
while ((line=bufferedReader.readLine())!=null){
printWriter.println(line);
printWriter.flush();
}
//关闭Reader和PrintWriter
bufferedReader.close();
printWriter.close();

}
}

image-20210728181636475

  • 使用PrintWriterprintln函数可以自动换行,方便快捷:)

FileSystemProvider读写文件

JDK7新增的NIO.2的java.nio.file.spi.FileSystemProvider,利用FileSystemProvider我们可以利用支持异步的通道(Channel)模式读取文件内容。

读文件

1
2
3
4
5
6
7
public class providerTest {
public static void main(String[] args) throws Exception{
Path path = Paths.get("src/test.txt");
byte[] bytes = Files.readAllBytes(path);
System.out.println(new String(bytes));
}
}

image-20210728184307845

写文件

1
2
3
4
5
6
7
public class providerTest {
public static void main(String[] args) throws Exception{
Path path = Paths.get("src/test.txt");
String content="Hello\nWorld";
Files.write(path,content.getBytes(StandardCharsets.UTF_8));
}
}

image-20210728184408782

文件读写总结

读取方式有:

1
2
3
4
FileInputStream
BufferedInputStream
InputStreamReader
BufferedReader

写入方式有:

1
2
3
4
5
FileOutputStream
BufferedOutputStream
OutputStreamWriter
BufferedWriter
PrintWriter

另外的还有RandomAccessFileFileSystemProvider

注意:带有er的类的操作对象是字符流,没有的操作的是字节流

CATALOG
  1. 1. 文件的编码
  2. 2. File类常用API
  3. 3. IO/NIO
    1. 3.1. RandomAcessFile
    2. 3.2. FileInputStream&&FileOutputStream
      1. 3.2.1. FileInputStream读取文件
      2. 3.2.2. FileOutputStream写入文件
    3. 3.3. BufferedInputStream&&BufferedOutputStream
      1. 3.3.1. BufferedInputStream读取文件内容
      2. 3.3.2. BufferedOutputStream写入文件内容
    4. 3.4. InputStreamReader&&OutputStreamWriter
      1. 3.4.1. InputStreamReader读取文件
      2. 3.4.2. OutputStreamWriter写入文件
    5. 3.5. BufferedReader&BufferedWriter&PrintWriter
    6. 3.6. FileSystemProvider读写文件
    7. 3.7. 文件读写总结