1.本文概述
本文讲解IO系列有关字符流对象部分的内容。
字符流主要包含以下四个组成对象:
FileReader
FileWriter
BufferedReader
BufferedWriter
2.Writer部分
1.Writer概述
常用方法:
abstract void close()
关闭此流,但要先刷新它。即会在关闭前先调用flush方法。
abstract void flush()
刷新该流的缓冲。
void write(char[] cbuf)
写入字符数组。
abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
void write(int c)
写入单个字符。
void write(String str)
写入字符串。
void write(String str, int off, int len)
写入字符串的某一部分。
说明:Writer是一个抽象类,无法直接实例化,因此一般使用其间接子类FileWriter。
2.FileWriter
FileWriter概述
FileWriter是用来写入字符文件的便捷类
常用构造方法:
FileWriter(File file)
根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(File file, boolean append)
根据给定的 File 对象构造一个 FileWriter 对象。如果append为true,则将数据写入文件末尾处,而不是覆盖整个文件。
说明:关于参数中的File对象部分知识将在后续博文中讲解,具体可参考:
FileWriter(String fileName)
根据给定的文件名构造一个 FileWriter 对象。
FileWriter(String fileName, boolean append)
根据给定的文件名以及指示是否附加写入数据的boolean值来构造 FileWriter 对象。如果append为true,则将数据写入文件末尾处,而不是覆盖整个文件。
说明:FileWriter的方法与Writer中的方法一致,并没有特有方法,此处不再对其方法进行列举。
向文件中写出数据
本例中用到的构造方法:
FileWriter(String fileName)
根据给定的文件名构造一个 FileWriter 对象。
说明:在表示文件路径时,分隔符需要使用双反斜杠”\”
步骤:
1.创建一个FileWriter对象,该对象初始化时必须要明确被操作的文件,并且该文件会被创建到指定目录下。若该目录下已有同名文件,则该文件将被覆盖。
2.调用write方法将字符串写入流中
3.刷新流对象中的缓冲数据,将数据写入目标文件中
说明:输出流在关闭前可以多次调用write
以及flush
方法
4.关闭流
说明:该方法会在关闭流前先调用flush方法刷新缓存,调用后流就被彻底关闭了。
示例代码:
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
public static void main(String[] args) {
try {
//实例化字符输出流对象
FileWriter fw=new FileWriter("G:\\test.txt");
//向指定文件中输出数据
fw.write("test:");
fw.write("这是一份测试文件");
//刷新流缓存
fw.flush();
//关闭流
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
程序运行结果
1.png
3.IO异常处理方式
与输入输出流相关的操作都可能抛出IOException异常,这需要我们进行一些合适的异常处理。
以下为一个标准的IOException处理示例:
示例代码:
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
public static void main(String[] args) {
//声明字符输出流对象并初始化
FileWriter fw=null;
try {
//实例化字符输出流对象
fw=new FileWriter("G:\\test.txt",true);
//向指定文件中输出数据
fw.write("\r\ntest:这是在原有基础上添加数据!");
//刷新流缓存
fw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally{
//当fw存在时才关闭流,防止空指针异常
if(fw!=null)
try {
//关闭流
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4.对已存在的文件进行续写
使用FileWriter另一个构造方法:
FileWriter(String fileName, boolean append)
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
说明:当append传入参数为true时,表示不覆盖已有的文件,而在已有文件的末尾续写数据。
示例代码:
//部分代码
public static void main(String[] args) {
//声明字符输出流对象并初始化
FileWriter fw=null;
try {
//实例化字符输出流对象
fw=new FileWriter("G:\\test.txt",true);
//向指定文件中输出数据
fw.write("\r\ntest:这是在原有基础上添加数据!");
//刷新流缓存
fw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally{
//当fw存在时才关闭流,防止空指针异常
if(fw!=null)
try {
//关闭流
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
程序运行结果:
2.png
小技巧:如果想要实现文本换行的效果,可以使用转义字符”\n”,但为了使windows下的记事本识别换行操作,需要使用”\r\n”
3.Reader部分
1.Reader概述
常用方法:
abstract void close()
关闭该流并释放与之关联的所有资源。
int read()
读取单个字符。如果已到达流的末尾,则返回-1
int read(char[] cbuf)
将字符读入数组。如果已到达流的末尾,则返回-1
abstract int read(char[] cbuf, int off, int len)
将字符读入数组的某一部分。如果已到达流的末尾,则返回-1。off是开始位置的索引,len是要读取的最大字符数
long skip(long n)
跳过字符int read(CharBuffer target)
试图将字符读入指定的字符缓冲区。如果已到达流的末尾,则返回-1 。
void reset()
重置该流。
说明:Reader是一个抽象类,无法直接实例化,因此一般使用其间接子类FileReader。
2.FileReader
FileReader概述
FileReader用来读取字符文件的便捷类。
构造方法:
FileReader(File file)
在给定从中读取数据的 File 的情况下创建一个新 FileReader。
FileReader(String fileName)
在给定从中读取数据的文件名的情况下创建一个新 FileReader。
说明:FileWriter的方法与Writer中的方法一致,并没有特有方法,此处不再对其方法进行列举。
第一种文本文件读取方式
通过读取单个字节的方式获得文件中的所有数据。
步骤:
1.创建一个文件读取流对象,和指定名称的文件相关联。如果文件不存在,则会抛出FileNotFoundException
2.调用读取流对象的read方法
fr.read();
说明:当read
方法读取到流的末尾时,该方法返回-1。利用这一特点,可以通过while循环输出文件中的所有数据。
示例代码:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo {
public static void main(String[] args) {
FileReader fr=null;
try {
//实例化指向已存在文件的输入流对象
fr=new FileReader("G:\\test.txt");
int ch=0;
while((ch=fr.read())!=-1){
//将获取到的int型数据强转为char型输出
System.out.print((char)ch);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally{
if(fr!=null)
try {
//关闭流
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
程序运行结果:
test:这是一份测试文件
test:这是在原有基础上添加数据!
第二种文本文件读取方式
通过字符数组对文件中的数据进行读取。
步骤:
1.定义一个字符数组,用于存储读到的字符
2.创建一个文件输入流对象
3.通过read(char[])
方法将文件数据读入指定数组
说明:同样利用read
方法读取完毕返回-1的特性,可以使用while循环读取文件中的全部数据。注意要对获取到的数组进行有效范围截取,因为在循环的最后一次数据读取中,数组不一定被填满。此外,为了保持较高的读取效率,建议将用于接受数据的字符数组大小定义为1024的整数倍。
示例代码:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo {
public static void main(String[] args) {
FileReader fr=null;
try {
//实例化指向已存在文件的输入流对象
fr=new FileReader("G:\\test.txt");
//定义用于临时存储数据的字符数组
char[] bufArr=new char[1024];
//存储读取到的字节长度
int len;
while((len=fr.read(bufArr))!=-1){
//将获取到的char数组转化为字符串,注意对字符数组有效区域的截取
String endStr=new String(bufArr,0,len);
System.out.print(endStr);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally{
if(fr!=null)
try {
//关闭流
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
程序运行结果:
test:这是一份测试文件
test:这是在原有基础上添加数据!
说明:向控制台中打印数据时应该使用print方法,否则可能在错误的位置进行换行,影响输出效果
总结:以上两种方法推荐使用第二种。第二种方式由于具有字符数组作为缓冲区,相比之下读取效率更高
3.简单应用-复制文本文件
需求描述:将一个文本文件从E盘复制到D盘
复制的原理:其实就是将E盘下的文件数据读取后,输出到到D盘的指定文件中
步骤:
1.在D盘创建一个文件,用于存储E盘的文件数据
2.定义输入输出流和E盘指定文件关联
3.通过不断的读写完成数据存储
4.关闭流资源
第一种方式
示例代码:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyDemo {
public static void main(String[] args) {
FileWriter fw=null;
FileReader fr=null;
try {
//实例化输入输出流
fr=new FileReader("E:\\demo.txt");
fw=new FileWriter("D:\\demo.txt");
//将读取到的数据写入目标文件
int ch=0;
while((ch=fr.read())!=-1){
fw.write(ch);
//刷新流缓存
fw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
//关闭资源
if(fr!=null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
if(fw!=null)
try {
//关闭流
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
说明:每次读取一个字符就写入一个字符,效率较低。因此不建议使用这种方式。但如果目标文件较小倒也无所谓。
第二种方式
本例用到的方法:
int read(char[] cbuf)
将字符读入数组。如果已到达流的末尾,则返回 -1
abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
示例代码:
public static void main(String[] args) {
FileWriter fw=null;
FileReader fr=null;
try {
//实例化输入输出流
fr=new FileReader("E:\\demo.txt");
fw=new FileWriter("D:\\demo.txt");
//定义用于临时存储数据的字符数组
char[] bufArr=new char[1024];
//存储读取到的字节长度
int len=0;
while((len=fr.read(bufArr))!=-1){
//将字符数组的有效范围写入目标文件
fw.write(bufArr,0,len);
//刷新流缓存
fw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
//关闭资源
if(fr!=null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
if(fw!=null)
try {
//关闭流
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
说明:这种方式的效率较高,推荐使用
4.缓冲字符流
1.缓冲字符流概述
- 缓冲区的出现提高了对数据的读写效率
- 缓冲区要结合流才可以使用
- 缓冲流在流的基础上对流的功能进行了增强。
对应类:
BufferedWriter
BufferedReader
使用的缓冲区的时候的需要注意以下两点:
1.在使用缓冲区之前必须先有流对象,并将流对象作为参数构造一个缓冲区对象。
2.使用缓冲区时,一定要使用flush
方法。另外,关闭缓冲区实际上就是关闭缓冲区中的流对象,因此在使用close
对缓冲区进行关闭后,可以不必对流对象再调用close
方法进行关闭。
2.BufferedWriter
BufferedWriter用于将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入.可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
构造方法:
BufferedWriter(Writer out)
创建一个使用默认大小输出缓冲区的缓冲字符输出流。
BufferedWriter(Writer out, int sz)
创建一个使用给定大小输出缓冲区的新缓冲字符输出流。
常用方法:
void close()
关闭此流,但要先刷新它。
void flush()
刷新该流的缓冲。
void newLine()
写入一个行分隔符,即换行符。并且该操作是跨平台的。
void write(char[] cbuf)
写入字符数组。
void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
void write(int c)
写入单个字符。
void write(String str)
写入字符串。
void write(String s, int off, int len)
写入字符串的某一部分。
示例代码:
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufWriterDemo {
public static void main(String[] args) {
BufferedWriter bw=null;
try {
//通过传入FileWriter对象实例化BufferedWriter
bw=new BufferedWriter(new FileWriter("G:\\buf_test.txt"));
bw.write("test:这是通过缓冲流写入的数据");
//通过缓冲流的特有方法写入换行符
bw.newLine();
bw.write("test:通过缓冲流的方法写入了换行符");
//刷新流的缓存
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
finally{
if(bw!=null)
try {
//关闭流
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
程序运行结果:
3.png
3.BufferedReader
BufferedReader用于从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
构造方法:
BufferedReader(Reader in)
创建一个使用默认大小输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in, int sz)
创建一个使用指定大小输入缓冲区的缓冲字符输入流。
常用方法:
void close()
关闭该流并释放与之关联的所有资源。
int read()
读取单个字符。
int read(char[] cbuf, int off, int len)
将字符读入数组的某一部分。
String readLine()
读取一个文本行,包含该行内容的字符串,不包含任何行终止符。如果已到达流末尾,则返回 null。
long skip(long n)
跳过字符。
void reset()
将流重置到最新的标记。
示例代码:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class BufReaderDemo {
public static void main(String[] args) {
BufferedReader br=null;
try {
通过传入FileReader对象实例化BufferedReader
br=new BufferedReader(new FileReader("G:\\buf_test.txt"));
String str=null;
while((str=br.readLine())!=null){
//注意要使用println输出获取到的数据
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally{
if(br!=null)
try {
//关闭流
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
程序运行结果:
test:这是通过缓冲流写入的数据
test:通过缓冲流的方法写入了换行符
说明:对于获取的每行数据,需要通过println输出,因为readLine()返回的字符串中不包含换行符。
4.通过缓冲区复制文本文件
需求描述: 通过缓冲区将E盘指定文件复制到D盘
通过readLine()
和writeLine()
方法高效率读取和写出
注意:输出时使用newLine()方法写出换行符,因为readLine()返回的字符串中不包含换行符,只返回有效内容。
示例代码:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class BufCopyDemo {
public static void main(String[] args) {
BufferedWriter bw=null;
BufferedReader br=null;
try {
//实例化输入输出缓冲流
bw=new BufferedWriter(new FileWriter("D:\\buf_demo.txt"));
br=new BufferedReader(new FileReader("E:\\buf_demo.txt"));
String str=null;
while((str=br.readLine())!=null){
bw.write(str);
//输出换行符
bw.newLine();
//刷新缓冲输出流缓存
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
if(bw!=null)
try {
//关闭流
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
if(br!=null)
try {
//关闭流
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5.自定义一个简单的缓冲区Reader类
示例代码:
import java.io.FileReader;
import java.io.IOException;
public class MyReader {
private FileReader fr;//字符流,用于数据的读取
//构造方法
public MyReader(FileReader fr){
this.fr=fr;
}
//读取整行数据的自定义方法
public String MyReadLine() throws IOException{
//实例化一个StringBuilder对象用来临时存储数据
StringBuilder strBlr=new StringBuilder();
int ch=0;
while((ch=fr.read())!=-1){
//读取到换行符时执行的操作
if(ch=='\r')
continue;
//读取到换行符时执行的操作
if(ch=='\n')
return strBlr.toString();
else
strBlr.append((char)ch);
}
//当读取的文件中仅有一行数据且没有换行符时,返回读取到的字符串
if(strBlr.length()!=0)
return strBlr.toString();
return null;
}
//关闭流的方法
public void close() throws IOException{
fr.close();
}
}
注意:以上的自定义类仅实现了缓冲流的两个功能,可以利用装饰设计模式将代码优化。令该类继承Reader接口,并且通过子类实现父类中的抽象方法。优化后的代码如下。
示例代码:
import java.io.FileReader;
import java.io.IOException;
public class MyReader {
private FileReader fr;//字符流,用于数据的读取
//构造方法
public MyReader(FileReader fr){
this.fr=fr;
}
//读取整行数据的自定义方法
public String MyReadLine() throws IOException{
//实例化一个StringBuilder对象用来临时存储数据
StringBuilder strBlr=new StringBuilder();
int ch=0;
while((ch=fr.read())!=-1){
//读取到换行符时执行的操作
if(ch=='\r')
continue;
//读取到换行符时执行的操作
if(ch=='\n')
return strBlr.toString();
else
strBlr.append((char)ch);
}
//当读取的文件中仅有一行数据且没有换行符时,返回读取到的字符串
if(strBlr.length()!=0)
return strBlr.toString();
return null;
}
//关闭流的方法
public void close() throws IOException{
fr.close();
}
}
5.LineNumberReader
LineNumberReader
是跟踪行号的缓冲字符输入流。
构造方法:
LineNumberReader(Reader in)
使用默认输入缓冲区的大小创建新的行编号 reader。
LineNumberReader(Reader in, int sz)
创建新的行编号 reader,将字符读入给定大小的缓冲区。
常用方法:
int getLineNumber()
获得当前行号。
void setLineNumber(int lineNumber)
设置当前行号。
int read()
读取单个字符。
int read(char[] cbuf, int off, int len)
将字符读入数组中的某一部分。
String readLine()
读取文本行。
long skip(long n)
跳过字符。
void reset()
将该流重新设置为最新的标记。
说明:LineNumberReader是基于Reader类,通过装饰设计模式的一个装饰类
示例代码:
//部分代码
public static void main(String[] args) {
LineNumberReader lnr=null;
try {
//实例化LineNumberReader对象
lnr=new LineNumberReader(new FileReader("G:\\line_test.txt"));
String str=null;
while((str=lnr.readLine())!=null){
//获取行号
int num=lnr.getLineNumber();
if(num==6)
lnr.setLineNumber(7);
System.out.println(num+" "+str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally{
if(lnr!=null)
try {
lnr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
程序运行结果:
1 test:这是用于测试LineNumberReader的文件
2 LineNumberReader可以输出数据的行号
3 就像你现在看到的这样
4 么么哒
5 而且还可以设置行号
6 就像这样
8 我其实是第七行,不是第八行
相关阅读: