字符流
# 字符流
与字节流类似,字符流也有两个抽象基类,分别是Reader
和 Writer
。其他的字符流实现类都是继承了这两个类。
以Reader
为例,它的主要实现子类如下图:
当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
小贴士
字符流,只能操作文本文件,不能操作图片,视频等非文本文件。当我们单纯读或者写文本文件时 使用字符流 其他情况使用字节流。
# 1. 字符输入流【Reader】
java.io.Reader
抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
public int read()
: 从输入流读取一个字符。 虽然读取了一个字符,但是会自动提升为int类型。返回该字符的Unicode编码值。如果已经到达流末尾了,则返回-1。public int read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。每次最多读取cbuf.length个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回-1。public int read(char[] cbuf,int off,int len)
:从输入流中读取一些字符,并将它们存储到字符数组 cbuf中,从cbuf[off]开始的位置存储。每次最多读取len个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回-1。public void close()
:关闭此流并释放与此流相关联的任何系统资源。
小贴士:close方法,当完成流的操作时,必须调用此方法,释放系统资源。
# 2. FileReader类
FileReader
提供了几种构造方法,用于根据不同的需求创建读取文件的流对象。常用的构造方法包括:
FileReader(String fileName)
:- 通过指定文件路径来创建
FileReader
对象。 - 参数:
fileName
是文件的路径,类型为String
。 - 该构造方法会根据提供的路径打开文件。如果文件不存在,将抛出
FileNotFoundException
。
FileReader fr = new FileReader("e:/sort.txt");
1- 通过指定文件路径来创建
FileReader(File file)
:- 通过指定的
File
对象来创建FileReader
对象。 - 参数:
file
是一个File
类型的对象。 - 该构造方法允许我们使用一个
File
对象来指定文件,而不是直接使用字符串路径。类似地,如果文件不存在,会抛出FileNotFoundException
。
File file = new File("e:/sort.txt"); FileReader fr = new FileReader(file);
1
2- 通过指定的
# 1. 单字符读取示例
- 创建
FileReader
对象: 指定要读取的文件路径。 - 逐字符读取: 使用
read()
方法逐个读取字符,直到返回-1
(文件末尾)。 - 关闭流: 使用
close()
方法释放资源。
代码示例:逐字符读取
import java.io.FileReader;
import java.io.IOException;
/**
* 示例:使用 FileReader 逐个字符读取文件内容。
*/
public class FileReaderSingleCharExample {
public static void main(String[] args) {
FileReader fr = null;
try {
// Step 1: 创建 FileReader 对象,指定文件路径
fr = new FileReader("e:/sort.txt");
int data; // 用于存储读取到的字符(Unicode 编码值)
// Step 2: 逐个字符读取,直到文件末尾(返回 -1)
while ((data = fr.read()) != -1) {
// 将 Unicode 编码转换为字符并打印
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// Step 3: 确保流资源被正确关闭
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
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
# 2. 批量读取字符到数组
- 创建
FileReader
对象: 指定要读取的文件路径。 - 创建字符数组: 用于存储读取的字符数据。
- 批量读取: 使用
read(char[] cbuf)
方法将多个字符读取到数组中,返回实际读取的字符数。 - 关闭流: 使用
close()
方法释放资源。
代码示例:批量读取字符
import java.io.FileReader;
import java.io.IOException;
/**
* 示例:使用 FileReader 批量读取字符到数组中。
*/
public class FileReaderCharArrayExample {
public static void main(String[] args) {
FileReader fr = null;
try {
// Step 1: 创建 FileReader 对象,指定文件路径
fr = new FileReader("e:/sort.txt");
// Step 2: 创建字符数组作为缓冲区
char[] buffer = new char[1024];
int len; // 每次读取的字符数
// Step 3: 批量读取字符到数组,直到文件末尾(返回 -1)
while ((len = fr.read(buffer)) != -1) {
// 将读取的字符数组转换为字符串并打印,只打印实际读取的部分
System.out.print(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// Step 4: 确保流资源被正确关闭
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
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
# 3. 指定起始位置和长度读取
- 创建
FileReader
对象: 指定要读取的文件路径。 - 创建字符数组: 用于存储读取的字符数据。
- 指定读取位置和长度: 使用
read(char[] cbuf, int off, int len)
方法从指定位置开始读取指定长度的字符数据。 - 关闭流: 使用
close()
方法释放资源。
代码示例:指定位置和长度读取
import java.io.FileReader;
import java.io.IOException;
/**
* 示例:使用 FileReader 从指定位置和长度读取字符数据。
*/
public class FileReaderSpecificPositionExample {
public static void main(String[] args) {
FileReader fr = null;
try {
// Step 1: 创建 FileReader 对象,指定文件路径
fr = new FileReader("e:/sort.txt");
// Step 2: 创建字符数组作为缓冲区
char[] buffer = new char[1024];
// Step 3: 从字符数组的指定位置开始,读取固定长度的字符
int len = fr.read(buffer, 0, 10); // 从 buffer[0] 开始读取 10 个字符
// 将读取的字符数组转换为字符串并打印
System.out.print(new String(buffer, 0, len));
} catch (IOException e) {
e.printStackTrace();
} finally {
// Step 4: 确保流资源被正确关闭
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
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
总结
Reader
是字符输入流的基类,提供了读取字符的基础方法。FileReader
是用于从文件中读取字符数据的具体实现类,适合读取文本文件。- 逐字符读取适合处理小数据量或逐步解析的场景,批量读取则适合处理大文件或需要提高效率的场景。
- 在操作完流后,务必调用
close()
方法以释放系统资源,避免资源泄露。
# 3. 字符输出流【Writer】
java.io.Writer
抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。
public void write(int c)
写入单个字符。public void write(char[] cbuf)
写入字符数组。public void write(char[] cbuf, int off, int len)
写入字符数组的某一部分,off是数组的开始索引,len是写的字符个数。public void write(String str)
写入字符串。public void write(String str, int off, int len)
写入字符串的某一部分,off字符串的开始索引,len写的字符个数。public void flush()
刷新该流的缓冲。public void close()
关闭此流,但要先刷新它。
# 4. FileWriter类
java.io.FileWriter
类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
FileWriter(File file)
: 创建一个新的 FileWriter,给定要读取的File对象。FileWriter(String fileName)
: 创建一个新的 FileWriter,给定要读取的文件的名称。
当你创建一个流对象时,必须传入一个文件路径,类似于FischolarutputStream。如果文件不存在,则会自动创建。如果文件已经存在,则会清空文件内容,写入新的内容。
# 1. 单字符写入示例
- 创建
FileWriter
对象: 指定要写入的文件路径。 - 写入单个字符: 使用
write(int c)
方法写入字符数据。 - 关闭流: 使用
close()
方法释放资源。
代码示例:写入单个字符
import java.io.FileWriter;
import java.io.IOException;
/**
* 示例:使用 FileWriter 写入单个字符到文件中。
*/
public class FileWriterSingleCharExample {
public static void main(String[] args) {
FileWriter fw = null;
try {
// Step 1: 创建 FileWriter 对象,指定文件路径
fw = new FileWriter("fw.txt");
// Step 2: 写入单个字符
fw.write(97); // 写入字符 'a'
fw.write('b'); // 写入字符 'b'
fw.write('C'); // 写入字符 'C'
fw.write(30000); // 写入字符对应的 Unicode 码点为 30000 的字符
} catch (IOException e) {
e.printStackTrace();
} finally {
// Step 3: 关闭流,确保数据完全写入文件
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
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
# 2. 写入字符数组
- 创建
FileWriter
对象: 指定要写入的文件路径。 - 写入字符数组: 使用
write(char[] cbuf)
方法写入整个字符数组或部分数据。 - 关闭流: 使用
close()
方法释放资源。
代码示例:写入字符数组
import java.io.FileWriter;
import java.io.IOException;
/**
* 示例:使用 FileWriter 写入字符数组到文件中。
*/
public class FileWriterCharArrayExample {
public static void main(String[] args) {
FileWriter fw = null;
try {
// Step 1: 创建 FileWriter 对象,指定文件路径
fw = new FileWriter("fw.txt");
// Step 2: 写入字符数组
char[] chars = "程序员scholar".toCharArray();
fw.write(chars); // 写入整个字符数组
// Step 3: 写入字符数组的一部分
fw.write(chars, 1, 2); // 从索引 1 开始,写入 2 个字符
} catch (IOException e) {
e.printStackTrace();
} finally {
// Step 4: 关闭流,确保数据完全写入文件
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
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
# 3. 写入字符串
- 创建
FileWriter
对象: 指定要写入的文件路径。 - 写入字符串: 使用
write(String str)
方法写入整个字符串或部分数据。 - 关闭流: 使用
close()
方法释放资源。
代码示例:写入字符串
import java.io.FileWriter;
import java.io.IOException;
/**
* 示例:使用 FileWriter 写入字符串到文件中。
*/
public class FileWriterStringExample {
public static void main(String[] args) {
FileWriter fw = null;
try {
// Step 1: 创建 FileWriter 对象,指定文件路径
fw = new FileWriter("fw.txt");
// Step 2: 写入字符串
String msg = "程序员scholar";
fw.write(msg); // 写入整个字符串
// Step 3: 写入字符串的一部分
fw.write(msg, 1, 2); // 从索引 1 开始,写入 2 个字符
} catch (IOException e) {
e.printStackTrace();
} finally {
// Step 4: 关闭流,确保数据完全写入文件
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
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
# 4. 文件追加写入
FileWriter
类提供了两种构造方法来支持文件的续写,即在文件的末尾追加内容而不是覆盖原始内容。
public FileWriter(File file,boolean append)
: 创建文件输出流以写入由指定的 File对象表示的文件。public FileWriter(String fileName,boolean append)
: 创建文件输出流以指定的名称写入文件。
这两个构造方法,参数中都需要传入一个boolean类型的值,true
表示追加数据,false
表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了,代码使用演示:
操作类似于FileOutputStream。
import java.io.FileWriter;
import java.io.IOException;
/**
* 示例:使用 FileWriter 进行文件追加写入。
*/
public class FileWriterAppendExample {
public static void main(String[] args) {
FileWriter fw = null;
try {
// Step 1: 创建 FileWriter 对象,设置 append 为 true 开启追加模式
fw = new FileWriter("fw.txt", true);
// Step 2: 写入数据,将数据追加到文件末尾
fw.write("程序员scholar真棒");
// 输出提示信息,表明数据已成功追加写入
System.out.println("数据追加写入完成!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// Step 3: 关闭流,确保数据完全写入文件
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
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
# 5. 换行操作
在使用FileWriter
类写入文件时,有时需要在写入的字符串之间添加换行符以实现换行。这可以通过写入系统相关的换行符来完成。在Windows系统中,换行符为\r\n
;在Linux和Unix系统中,换行符为\n
;而在Mac系统中(旧版本),换行符为\r
。为了跨平台兼容性,建议使用System.getProperty("line.separator")
来获取系统的换行符。
以下示例展示了如何使用FileWriter
类进行写入操作,并在两段字符串之间添加换行符以实现换行:
import java.io.FileWriter;
import java.io.IOException;
/**
* 示例:使用 FileWriter 写入数据并添加换行符。
*/
public class FileWriterNewLineExample {
public static void main(String[] args) {
FileWriter fw = null;
try {
// Step 1: 创建 FileWriter 对象,指定文件路径
fw = new FileWriter("fw.txt");
// Step 2: 写入数据并添加换行符
fw.write("程序员scholar");
fw.write(System.getProperty("line.separator")); // 添加系统相关的换行符
// 再次写入数据
fw.write("再次欢迎程序员scholar");
System.out.println("数据写入完成,并正确换行。");
} catch (IOException e) {
e.printStackTrace();
} finally {
// Step 3: 关闭流,确保数据完全写入文件
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
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
# 6. 关闭和刷新
【注意】FileWriter与FileOutputStream不同。因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush
方法了。
flush
:刷新缓冲区,流对象可以继续使用。close
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
代码使用演示:
import java.io.FileWriter;
import java.io.IOException;
/**
* 示例:使用 FileWriter 进行数据的刷新与关闭操作。
*/
public class FileWriterFlushExample {
public static void main(String[] args) {
FileWriter fw = null;
try {
// Step 1: 创建 FileWriter 对象,指定文件路径
fw = new FileWriter("fw.txt");
// Step 2: 写出数据并演示 flush 方法
fw.write('刷'); // 写出字符
fw.flush(); // 刷新缓冲区,确保数据写入文件
fw.write('新'); // 继续写出字符,流仍然可用
fw.flush(); // 再次刷新
fw.write('关'); // 写出字符
fw.close(); // 关闭流,释放资源
// 尝试在流关闭后写出数据,这会引发 IOException
fw.write('闭'); // 抛出 IOException,因为流已经关闭
} catch (IOException e) {
System.out.println("发生异常:" + e.getMessage());
} finally {
// 确保流在出现异常时被正确关闭
if (fw != null) {
try {
fw.close(); // 再次尝试关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
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
小贴士:即便是flush方法写出了数据,操作的最后还是要调用close方法,释放系统资源。