目录
1. 文件字节流:FileInputStream 与 FileOutPutStream
2. 文件字符流:FileReader 与 FileWriter
引言
上篇文章提到,IO 流根据其传输数据单元的不同分为字节流与字符流。字节流主要用于操作 byte 类型的数据,流中最小的数据单元为字节,所有的字节流都继承了它们的抽象父类 OutputStream 与 InputStream。
但对于含有中文字符的文本文件,采用的编码格式不同,每个中文字符占用的字节个数就不同。例如 UTF-8 每个中文字符占 3 个字节,而 GBK 每个中文字符占两个字节,因此若采用字节流按字节来读,每个中文字符就要被分成两次或者三次来读,可能无法正常获取其中所包含的信息。
因此对于含有中文以及各种符号的文本文件,就可以使用字符流来读取。字符流的最小数据单元为字符,无论何种编码格式,字母、数字、符号以及各种文字都会以一个一个字符的形式在设备与程序之间传输,这样就无需考虑字节转换为字符的问题。
最后特别要注意的是:任何流在使用完之后都必须使用 close( ) 方法关闭!
void
close()
关闭此流并释放与此流有关的所有系统资源。
下面是主要的学习内容列表:
(1)两组节点流:字节流 FileInputStream 与 FileOutPutStream、字符流 FileReader 与 FileWriter
(2)两组缓冲流:字节流 BufferedInputStream 与 BufferedOutputInstream、字符流 BufferedReader 与 BufferedWriter
(3)一组转换流:字符流 InputStreamReader 与 OutputStreamReader
1. 文件字节流:FileInputStream 与 FileOutPutStream
FileInputStream 与 FileOutPutStream 是最为常用且基础的节点流,大多的处理流也都使用了此节点流来构建,因此学习文件字节流是非常有必要的。
(1)使用 FileOutputStream 写文件
void | write(byte[] b) 将 b.length 个字节从指定 byte 数组写入此文件输出流中。 |
void | write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 |
void | write(int b) 将指定字节写入此文件输出流。 |
@Test
public void write() throws IOException {
//创建文件
File file = new File("src\\com\\java\\day08\\text.txt");
if(!file.exists()){
file.createNewFile();
}
//创建一个字节流
OutputStream outputStream = new FileOutputStream(file);
//1.写入一个字节,8位二进制,也就是十进制-128到127之间
outputStream.write(97);
//2.写入多个字节(字节数组)
String str = "Hello World!My name is smallOrange," +
"It's so lucky that I live with you.";
byte[] bytes = str.getBytes();//把字符串转换为字节数组
outputStream.write(bytes);
//3.只写入字节数组一部分内容,如World,起始位置6,长度5
outputStream.write(bytes,6,5);
//关闭资源
outputStream.close();
}
从上图可以看出,整数 97 根据 ASCⅡ 编码表转换为了字符 'a' ,字符串也通过字节数组写入了 .txt 文件,这就实现了对文件的写入操作。但同时也存在两个问题:
①在写入文件时如何换行?
②每次写入文件都是覆盖以前的数据,如何让它接着以前数据继续写入?
FileOutputStream(File file, boolean append)
创建一个向指定File
对象表示的文件中写入数据的文件输出流。从构造方法可以观察到在此方法有一个名为 append 的参数,此参数默认情况下为 false ,当把它设为 true 时就会追加写入数据,而不是直接覆盖;而换行只需要在需要换行的地方写入字符串 "\r\n" 即可。还需要注意的是,流在用完之后必须关闭!
此时在原来文件中追加写入一些数据:数据换行并且追加写入在了原数据之后。
@Test
public void write() throws IOException {
//创建文件
File file = new File("src\\com\\java\\day08\\text.txt");
if(!file.exists()){
file.createNewFile();
}
//创建一个字节流,追加写入
OutputStream outputStream = new FileOutputStream(file,true);
//1.换行
outputStream.write("\r\n".getBytes());
//2.写入多个字节(字节数组)
String str = "Hello World!My name is smallOrange," +
"It's so lucky that I live with you.";
byte[] bytes = str.getBytes();//把字符串转换为字节数组
outputStream.write(bytes);
//关闭资源
outputStream.close();
}
(2)使用 FileInputStream 读文件
int | available() 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。 |
int | read() 从此输入流中读取一个数据字节。 |
int | read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 |
int | read(byte[] b, int off, int len) 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 |
@Test
public void read1() throws IOException {
//创建文件路径
File file = new File("src\\com\\java\\day08\\text.txt");
//使用字节流读文件
InputStream stream = new FileInputStream(file);
//1.只读一个字节
System.out.println("1.每次读一个字节");
//无参数的read方法返回的是字节数据,若没有数据返回-1
int value = stream.read();
System.out.println(value);
//估计剩余要读的字节数
System.out.println("剩余字节数:" + stream.available());
//2.一次性读多个字节
System.out.println("2.一次读多个字节");
byte[] bytes = new byte[1024];//1kb
//带参数的read方法返回的是读到的字节长度,若没有读到数据返回-1
int len = stream.read(bytes);
//把保存数据的字节数组转为字符串输出,避免打印无效的数据
System.out.println(new String(bytes,0,len));
//打印此次读到的长度
System.out.println("读到的字节长度:" + len);
//关闭资源
stream.close();
}
测试结果:
1.每次读一个字节
72
剩余字节数:13
2.一次读多个字节
ello World!
读到的字节长度:13
2. 文件字符流:FileReader 与 FileWriter
文件字符流与文件字节流一样,也是字符流中最为基础的节点流,其与字节流的使用方式基本相同。但需要特别注意的是,抽象父类字符输出流 Writer 是自带缓冲区的 ,默认为长度 1024 的 char 数组。当一次性写入的数据量超过缓冲区的大小,就会自动刷新缓冲区直到剩余数据量小于 1024 个字符 。此时就会将剩余的数据存入缓冲区但不会刷新,需要使用者手动调用 flush( ) 方法刷新缓冲区,因此 flush( ) 方法适用于在缓冲区没满的情况下强制刷新缓冲区。
(1)使用 FileWriter 写文件
void | close() 关闭此流,但要先刷新它。 |
void | flush() 刷新该流的缓冲。 |
void | write(char[] cbuf) 写入字符数组。 |
void | write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 |
void | write(int c) 写入单个字符。 |
void | write(String str) 写入字符串。 |
void | write(String str, int off, int len) 写入字符串的某一部分。 |
@Test
public void write() throws IOException {
String pathName = "src\\com\\java\\day09\\file.txt";
//创建文件字符输出流,设置追加写入
FileWriter writer = new FileWriter(pathName,true);
//写入一个数字,会自动转换为对应字符
writer.write(97);
//写入一个字符数组
writer.write(new char[]{'我','a','1','2','3'});
//写入一个字符串
writer.write("\n我是一个小橘子!");
//必须刷新缓冲区,否则数据无法写入到文件,而是保留在缓冲区
writer.flush();
//关闭资源
writer.close();
}
(2)使用 FileReader 读文件
int | read() 读取单个字符。 |
int | read(char[] cbuf) 将字符读入数组。 |
int | read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。 |
long | skip(long n) 跳过字符。 |
@Test
public void read() throws IOException {
String pathName = "src\\com\\java\\day09\\file.txt";
//创建文件字符输出流,设置追加写入
FileReader reader = new FileReader(pathName);
//读一个字符:返回字符对应的数字
int value = reader.read();
System.out.println(value);
System.out.println();
//跳过五个字符
reader.skip(5);
//一次读多个字符:返回读取字符长度
char[] chars = new char[1024];
int len = reader.read(chars);
System.out.println(new String(chars));
System.out.println("长度:"+len);
//关闭资源
reader.close();
}
97
我是一个小橘子!
长度:9 //换行符也算一个字符