当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
java.io.Reader
抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。下面是Reader
的体系:
public abstract class Reader implements Readable, Closeable
它是个抽象类,因此不能被实例化。
Reader
提供了两个构造方法供子类使用
protected Reader()
protected Reader(Object lock)
它定义了字符输入流的基本共性功能方法。
public void close()
:关闭此流并释放与此流相关联的任何系统资源。public int read()
: 从输入流读取一个字符。public int read(char[] cbuf)
: 从输入流中读取一些字符public abstract int read(char[] cbuf, int off, int len)
: 从输入流中读取一些字符public int read(CharBuffer target)
:将字符读入指定的字符缓冲区public void mark(int readAheadLimit)
:标记流中的位置boolean markSupported()
: 是否支持 mark
public boolean ready()
: 流是否准备好被读取public void reset()
: 重置流public long skip(long n)
: 跳过字符由于public abstract int read(char[] cbuf, int off, int len)
是抽象方法,所以所有子类必须实现此方法。
JDK10
提供public long transferTo(Writer out)
用于写出读取到的字符
java.io.FileReader
类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
JDK11
提供的构造方法中可以传入指定编码
public class FileReader extends InputStreamReader
系统默认字符编码:
Charset.defaultCharset(); // 系统默认编码
eclipse
中默认为GBK
jetbrains idea
中默认UTF8
编辑器的默认编码是可以更改的
FileReader(File file)
: 创建一个新的 FileReader
,给定要读取的File对象。FileReader(String fileName)
: 创建一个新的 FileReader
,给定要读取的文件的名称。JDK11
新增的构造
FileReader(File file, Charset charset)
FileReader(String fileName, Charset charset)
例子:
File file = new File("D:\\temp\\a.txt");
FileReader reader = new FileReader(file);
FileReader reader1 = new FileReader("D:\\temp\\a.txt");
构造方法内部都是调用了父类的构造方法,传入一个FileInputStream
对象。
FileReader
中的所有方法都来自其父类InputStreamReader
,没有重写任何方法同时也没有扩展的方法。也就是说,学习了FileReader
同时也学习了InputStreamReader
.
read()
方法,每次可以读取一个字符的数据,提升为int
类型,读取到文件末尾,返回 -1 ,循环读取
package com.itlaobing.tor;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("D:/temp/reader.txt");
int ch = fr.read();//读取单个字符
System.out.println((char)ch);
fr.close();//关闭流
}
}
自行将上面代码修改为读取整个文本内容。
read(char[] cbuf)
,每次读取cbuf
的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回 -1
创建reader2.txt
内容为:
云创动力在西安学习中心
例子:
package com.itlaobing.tor;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest2 {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileReader fr = new FileReader("D:/temp/reader2.txt");
// 定义变量,保存有效字符个数
int len;
// 定义字符数组,作为装字符数据的容器
char[] cbuf = new char[2];
// 循环读取
while ((len = fr.read(cbuf)) != -1) {
System.out.println(new String(cbuf));
}
// 关闭资源
fr.close();
}
}
输入结果为:
云创
动力
在西
安学
习中
心中
我们发现多了一个字符,这是为什么呢?尝试将代码中的:
System.out.println(new String(cbuf));
修改为:
System.out.println(new String(cbuf, 0, len));
java.io.BufferedReader
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途。 它是一个缓冲流。
public class BufferedReader extends Reader
BufferedReader
提供了两个构造方法
//创建使用默认大小的输入缓冲区的缓冲字符输入流
public BufferedReader(Reader in)
//创建使用指定大小的输入缓冲区的缓冲字符输入流
public BufferedReader(Reader in, int sz)
BufferedReader
继承了Reader
中所有public
方法,还扩展了一个方法:
public String readLine() //读取一行文字
当readLine()
读取完毕时,返回null
创建bufferReader.txt
文件,并写入如下内容:
床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。
例子:
package com.itlaobing.demo.pm;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderTest {
public static void main(String[] args) throws IOException {
// 创建流对象
BufferedReader reader = new BufferedReader(new FileReader("D:/temp/bufferReader.txt"));
// 定义字符串,保存读取的一行文字
String line = null;
// 循环读取,读取到最后返回null
while ((line = reader.readLine()) != null) {
System.out.print(line);
System.out.println("------");
}
// 关闭流
reader.close();
}
}
java.io.InputStreamReader
是Reader
的子类,是从字节流到字符流的桥:它读取字节,并使用指定的Charset
将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
public class InputStreamReader extends Reader
InputStreamReader
中提供了四个构造方法:
//创建一个使用默认字符集的InputStreamReader
public InputStreamReader(InputStream in)
//创建一个使用命名字符集的InputStreamReader
public InputStreamReader(InputStream in, String charsetName)
//创建一个使用给定字符集的InputStreamReader
public InputStreamReader(InputStream in, Charset cs)
//创建一个使用给定字符集解码器的InputStreamReader
public InputStreamReader(InputStream in, CharsetDecoder dec)
其中常用的是前三个。
InputStreamReader
继承了Reader
中的所有public
方法,并扩展了getEncoding()
方法。
public String getEncoding()//获取使用的字符编码名称
其他方法在之前都有讲过,所以在此不再描述。
在创建InputStreamReader
对象时,指定编码字符集。
例子:
创建InputStreamReader.txt
文件,内容如下:
大家好,我是渣渣辉。
是兄弟就来**蓝月砍我。
package com.itlaobing.demo.pm;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class InputStreamReaderTest {
public static void main(String[] args) throws IOException {
// 定义文件路径,文件为gbk编码
String FileName = "D:\\temp\\InputStreamReader.txt";
// 创建流对象,默认UTF8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
// 创建流对象,指定GBK编码
InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName), "GBK");
// 定义变量,保存字符
int read;
// 使用默认编码字符流读取,乱码
while ((read = isr.read()) != -1) {
System.out.print((char) read);
}
isr.close();
System.out.println();
// 使用指定编码字符流读取,正常解析
while ((read = isr2.read()) != -1) {
System.out.print((char) read);
}
isr2.close();
}
}
输出结果如下:
��Һã����������ԡ�
���ֵܾ���**���¿��ҡ�
大家好,我是渣渣辉。
是兄弟就来**蓝月砍我。
java.io.Writer
抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。下面是Writer
的体系:
public abstract class Writer implements Appendable, Closeable, Flushable
Writer
提供了两个构造方法:
protected Writer();
protected Writer(Object lock)
Writer
类中提供了以下方法:
public abstract void close()
:关闭此输出流并释放与此流相关联的任何系统资源。public abstract void flush()
:刷新此输出流并强制任何缓冲的输出字符被写出。public void write(int c)
:写出一个字符。public void write(char[] cbuf)
:将 cbuf.length
字符从指定的字符数组写出此输出流。public abstract void write(char[] b, int off, int len)
:从指定的字符数组写出 len字符,从偏移量 off开始输出到此输出流。public void write(String str)
:写出一个字符串。java.io.FileWriter
类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
public class FileWriter extends OutputStreamWriter
FileWriter
类提供了5个构造方法:
//构造一个给定文件名的FileWriter对象
public FileWriter(String fileName);
//构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据
public FileWriter(String fileName, boolean append);
//给一个File对象构造一个FileWriter对象
public FileWriter(File file);
//给一个File对象构造一个FileWriter对象
public FileWriter(File file, boolean append);
//构造与文件描述符关联的FileWriter对象
public FileWriter(FileDescriptor fd)
常用的是前4个构造方法。
JDK11
新增的构造
public FileWriter(String fileName, Charset charset)
public FileWriter(String fileName, Charset charset, boolean append)
public FileWriter(File file, Charset charset)
public FileWriter(File file, Charset charset, boolean append)
FileWriter
类中的所有方法都来自其父类OutputStreamWriter
,没有重写任何方法同时也没有扩展的方法。也就是说,学习了FileWriter
同时也学习了OutputStreamWriter
。
write(int b)
方法,每次可以写出一个字符数据。
例子:
package com.itlaobing.demo.pm;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) throws IOException {
FileWriter writer = new FileWriter("D:/temp/fileWriter.txt");
writer.write(97); //a
writer.write('a'); //a
writer.write("你好"); //你好
writer.flush();//刷新缓冲区,流对象可以继续使用
writer.close();//关闭流,释放系统资源。关闭前会刷新缓冲区
}
}
执行完以上代码后,在D:/temp/
文件夹中会多出一个fileWriter.txt
文件,文件内容是:
aa你好
文件的字符编码集是系统默认的字符编码集,此处我们的默认编码集是UTF-8
write(char[] cbuf)
write(char[] b, int off, int len)
java.io.BufferedWriter
是将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。 可以指定缓冲区大小,或者可以接受默认大小。 默认值足够大,可用于大多数用途。是一个缓冲流。
public class BufferedWriter extends Writer
BufferedWriter
提供了两个构造方法:
//创建使用默认大小的输出缓冲区的缓冲字符输出流
public BufferedWriter(Writer out);
//创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区
public BufferedWriter(Writer out, int sz);
BufferedWriter
继承了Writer
中所有public
方法,还扩展了一个方法:
public void newLine();//写一个行分隔符,由系统属性定义符号
例子:
package com.itlaobing.tor;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferWriterTest {
public static void main(String[] args) throws IOException {
// 创建流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("d:/temp/bufferedWriter.txt"));
// 写出数据
bw.write("云创动力");
// 写出换行
bw.newLine();
bw.write("XX学院");
bw.newLine();
bw.write("IT特种兵");
bw.newLine();
// 释放资源
bw.close();
}
}
运行以上程序后,会在创建一个新文件
,编码字符集为默认编码字符集,此处为UTF-8
,内容为:
云创动力
XX学院
IT特种兵
java.io.OutputStreamWriter
,是Writer
的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
public class OutputStreamWriter extends Writer
OutputStreamWriter
提供了四个构造方法:
//创建一个使用默认字符编码的OutputStreamWriter
public OutputStreamWriter(OutputStream out);
//创建一个使用命名字符集的OutputStreamWriter。
public OutputStreamWriter(OutputStream out, String charsetName);
//创建一个使用给定字符集的OutputStreamWriter
public OutputStreamWriter(OutputStream out, Charset cs);
//创建一个使用给定字符集编码器的OutputStreamWriter
public OutputStreamWriter(OutputStream out, CharsetEncoder enc);
OutputStreamWrter
的方法大都继承自Writer
,除了getEncoding()
方法。
例子:
package com.itlaobing.tor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
public class OutputStreamWriterTest {
public static void main(String[] args) throws IOException {
FileOutputStream out = new FileOutputStream("d:/temp/outStreamWriter.txt");
Charset set = Charset.forName("utf-8");
OutputStreamWriter writer = new OutputStreamWriter(out, set);
System.out.println("编码格式为: " + writer.getEncoding());
writer.write("站在那别动,我去买橘子");
writer.close();
}
}
try
在JDK 7
之前我们使用try-catch
或try-catch-finally
来处理异常。在使用资源的时最后都需要关闭资源,所以一般我们在finally
处关闭资源。比如:
public static void main(String[] args) throws IOException {
FileOutputStream out = null;
OutputStreamWriter writer = null;
try {
out = new FileOutputStream("d:/temp/outStreamWriter.txt");
Charset set = Charset.forName("utf-8");
writer = new OutputStreamWriter(out, set);
System.out.println("编码格式为: " + writer.getEncoding());
writer.write("站在那别动,我去买橘子");
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭资源
writer.close();
out.close();
}
}
然而有的时候我们会忘记关闭资源。那么有没有更好的方法呢?
从jdk1.7
开始, Java 7
增强了try
语句的功能——它允许在try
关键字后跟一对圆括号()
,圆括号可以声明,初始化一个或多个资源,此处的资源指得是那些必须在程序结束时必须关闭的资源(比如流操作,数据库连接,网络连接等),try语句在该语句结束时自动关闭这些资源。这种称为try-with-resources
语句
Charset set = Charset.forName("utf-8");
try (FileOutputStream out = new FileOutputStream("d:/temp/outStreamWriter.txt"); OutputStreamWriter writer = new OutputStreamWriter(out, set);){
System.out.println("编码格式为: " + writer.getEncoding());
writer.write("站在那别动,我去买橘子");
} catch (IOException e) {
e.printStackTrace();
}
像这样的话,执行完会自动关闭,不用我们在finally中关闭,也再也不用担心忘记关闭了。
注意:只要这些资源实现类实现了Closeable
或AutoCloseable
接口,就可以自动关闭。几乎所有的资源都可以用这种方式实现自动关闭资源。
如果try()
里面有多个资源,用;
分开,资源的close
方法的调用顺序与它们的创建顺序相反。带有资源的try语句可以像一般的try语句一样具有catch和finally块。在try-with-resources语句中,任何catch或finally块都是在声明的资源被关闭后才会执行的