目录
1.1.2 FileOutputStream类写入数据到文件中
1.2.2 FileInputStream类读取数据read方法
第一章 字节流
1.1 字节输出流OutputStream
OutputStream为抽象类,是表示输出字节流的所有类的超类。操作的数据都是字节,定义了输出字节流的基本共性功能方法。
输出流中定义都是写write方法,如下图:
输入输出是相对于Java程序来说的,从Java程序到文件为输出,从文件到 Java程序为输入。
/*
* 字节输出流
* java.io.OutputStream 所有字节输出流的超类
* 作用: 从Java程序,写出文件
* 字节: 这样流每次只操作文件中的1个字节
* 写任意文件
*
* 方法都是写文入的方法
* write(int b) 写入1个字节
* write(byte[] b) 写入字节数组
* write(byte[] b,int,int)写入字节数组,int 开始写入的索引, int 写几个
* close() 方法,关闭流对象,释放与次流相关的资源
*
* 流对象,操作文件的时候, 自己不做,依赖操作系统
*/
public class OutputStreamDemo {
}
1.1.1 FileOutputStream类
因为OutputStream为抽象类,使用时必须创建其子类。OutputStream有很多子类,其中子类FileOutputStream可用来写入数据到文件。
FileOutputStream类,即文件输出流,是用于将数据写入 File
的输出流。
构造方法
1.1.2 FileOutputStream类写入数据到文件中
package day23.demo1;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* FileOutputStream
* 写入数据文件,学习父类方法,使用子类对象
*
* 子类中的构造方法: 作用:绑定输出的输出目的
* 参数:
* File 封装文件
* String 字符串的文件名
*
* 流对象使用步骤
* 1. 创建流子类的对象,绑定数据目的
* 2. 调用流对象的方法write写
* 3. close释放资源
*
* 流对象的构造方法,可以创建文件,如果文件存在,直接覆盖
*/
public class FileOutputStreamDemo {
public static void main(String[] args)throws IOException {
FileOutputStream fos = new FileOutputStream("d:\\a.txt");
//流对象的方法write写数据
//写1个字节
fos.write(97);//a
//写字节数组
byte[] bytes = {65,66,67,68};
fos.write(bytes);//ABCD
//写字节数组的一部分,开始索引,写几个
fos.write(bytes, 1, 2);//BC
//写入字节数组的简便方式
//写字符串
fos.write("hello".getBytes());//hello
//关闭资源
fos.close();
}
}
一个汉字占两个字节,一个数字占一个字节。
1.1.3 给文件中续写和换行
续写: FileOutputStream构造方法的第二个参数中,加入true,代表续写。(啥都不加代表覆盖)
换行:在文件中,写入换行符号“ \r\n” , \r\n 可以写在上一行的末尾, 也可以写在下一行的开头
构造方法:
package day23.demo1;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* FileOutputStream 文件的续写和换行问题
* 续写: FileOutputStream构造方法, 的第二个参数中,加入true
* 在文件中,写入换行,符号换行 \r\n
* \r\n 可以写在上一行的末尾, 也可以写在下一行的开头
*/
public class FileOutputStreamDemo1 {
public static void main(String[] args)throws IOException {
File file = new File("D:\\b.txt");
FileOutputStream fos = new FileOutputStream(file,true);
fos.write("hello\r\n".getBytes());
fos.write("world".getBytes());//.getBytes将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。
fos.close();
}
}
1.1.4 IO异常的处理
细节:
1. 保证流对象变量,作用域足够
2. catch里面,怎么处理异常?
输出异常的信息,目的看到哪里出现了问题
停下程序,重新尝试
3. 如果流对象建立失败了,需要关闭资源吗?
不需要,new 对象的时候,失败了,没有占用系统资源,因此不需要关闭资源。
释放资源的时候,先判断流对象,若为null,则创建失败,不需要释放资源。
若不是null,则对象建立成功,需要关闭资源
4.关闭文件需要写在finally里,若写在try中,若由于内存问题写入失败,抛出异常,后面的语句不再执行。
package day23.demo1;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* IO流的异常处理
* try catch finally
*
* 细节:
* 1. 保证流对象变量,作用域足够
* 2. catch里面,怎么处理异常
* 输出异常的信息,目的看到哪里出现了问题
* 停下程序,从新尝试
* 3. 如果流对象建立失败了,需要关闭资源吗
* 不需要,new 对象的时候,失败了,没有占用系统资源,因此不需要关闭资源。
* 释放资源的时候,先判断流对象,若为null,则创建失败,不需要释放资源。
* 若不是null,则对象建立成功,需要关闭资源
*/
public class FileOutputStreamDemo2 {
public static void main(String[] args) {
//try 外面声明变量,try 里面建立对象
FileOutputStream fos = null;
try{
fos = new FileOutputStream("s:\\a.txt");
fos.write(100);
}catch(IOException ex){
System.out.println(ex);
throw new RuntimeException("文件写入失败,重试");
}finally{
try{
if(fos!=null)
fos.close();
}catch(IOException ex){
throw new RuntimeException("关闭资源失败");
}
}
}
}
1.2 字节输入流InputStream
把硬盘文件中的数据,读取到内存中使用,我们通过InputStream可以实现。
InputStream为抽象类,是表示字节输入流的所有类的超类。定义了字节输入流的基本共性功能方法。
int read():读取一个字节并返回,没有字节时返回-1.
int read(byte[]): 读取一定量的字节数,并存储到字节数组中,返回读取到的字节数。
/*
* 字节输入流
* java.io.InputStream 所有字节输入流的超类
* 作用: 读取任意文件,每次只读取1个字节
* 读取的方法 read
* int read() 读取1个字节
* int read(byte[] b) 读取一定量的字节,存储到数组中
*/
public class InputStreamDemo {
}
1.2.1 FileInputStream类
InputStream有很多子类,其中子类FileInputStream可用来读取文件内容。
FileInputStream 从文件系统中的某个文件中获得输入字节。
构造方法:
1.2.2 FileInputStream类读取数据read方法
在读取文件中的数据时,调用read方法,实现从文件中读取数据
read()执行一次,就会自动读取下一个字节。
返回值,返回的是读取到的字节, 读取到结尾返回-1。
package day23.demo1;
import java.io.FileInputStream;
import java.io.IOException;
/*
* FileInputStream读取文件
*
* 构造方法: 为这个流对象绑定数据源
*
* 参数:
* File 类型对象
* String 对象
* 输入流读取文件的步骤
* 1. 创建字节输入流的子类对象
* 2. 调用读取方法read读取
* 3. 关闭资源
*
* read()方法,
* read()执行一次,就会自动读取下一个字节
* 返回值,返回的是读取到的字节, 读取到结尾返回-1
*/
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException{
FileInputStream fis = new FileInputStream("d:\\b.txt");
//读取一个字节,调用方法read 返回int
//使用循环方式,读取文件, 循环结束的条件 read()方法返回-1
int len = 0;//接受read方法的返回值
while( (len = fis.read()) != -1){
System.out.print((char)len);
}
//关闭资源
fis.close();
}
}
/*
* int i = fis.read();
System.out.println(i);
i = fis.read();
System.out.println(i);
i = fis.read();
System.out.println(i);
i = fis.read();
System.out.println(i);
*/
运行结果:
1.2.3 读取数据read(byte[])方法
在读取文件中的数据时,调用read方法,每次只能读取一个,太麻烦了,于是我们可以定义数组作为临时的存储容器,这时可以调用重载的read方法,一次可以读取多个字符。
FileInputStream读取文件
读取方法 int read(byte[] b) 读取字节数组
数组作用: 缓冲的作用, 提高效率
read返回的int:表示此次读取到有效的字节数
package day23.demo1;
import java.io.FileInputStream;
import java.io.IOException;
/*
* FileInputStream读取文件
* 读取方法 int read(byte[] b) 读取字节数组
* 数组作用: 缓冲的作用, 提高效率
* read返回的int,表示什么含义 读取到多少个有效的字节数
*/
public class FileInputStreamDemo1 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("d:\\b.txt");
//创建字节数组
byte[] b = new byte[1024];
int len = 0 ;
while( (len = fis.read(b)) !=-1){
System.out.print(new String(b,0,len));
}
fis.close();
}
}
/*
*
int len = fis.read(b);
System.out.println(new String(b));//ab
System.out.println(len);//2
len = fis.read(b);
System.out.println(new String(b));//cd
System.out.println(len);//2
len = fis.read(b);
System.out.println(new String(b));//ed
System.out.println(len);//1
len = fis.read(b);
System.out.println(new String(b));//ed
System.out.println(len);//-1
*/
1.3 字节流练习
1.3.1 复制文件
将数据源 c:\\a.txt,复制到 d:\\a.txt 数据目的
字节输入流,绑定数据源
字节输出流,绑定数据目的
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* 将数据源 c:\\a.txt
* 复制到 d:\\a.txt 数据目的
* 字节输入流,绑定数据源
* 字节输出流,绑定数据目的
*
* 输入,读取1个字节
* 输出,写1个字节
*/
public class Copy {
public static void main(String[] args) {
//定义两个流的对象变量
FileInputStream fis = null;
FileOutputStream fos = null;
try{
//建立两个流的对象,绑定数据源和数据目的
fis = new FileInputStream("c:\\t.zip");
fos = new FileOutputStream("d:\\t.zip");
//字节输入流,读取1个字节,输出流写1个字节
int len = 0 ;
while((len = fis.read())!=-1){
fos.write(len);
}
}catch(IOException ex){
System.out.println(ex);
throw new RuntimeException("文件复制失败");
}finally{
try{
if(fos!=null)
fos.close();
}catch(IOException ex){
throw new RuntimeException("释放资源失败");
}finally{
try{
if(fis!=null)
fis.close();
}catch(IOException ex){
throw new RuntimeException("释放资源失败");
}
}
}
}
}
上述复制文件有个问题,每次都从源文件读取一个,然后在写到指定文件,接着再读取一个字符,然后再写一个,一直这样下去。效率极低。
1.3.2 缓冲数组方式复制文件
上述代码复制文件效率太低了,并且频繁的从文件读数据,和写数据,能不能一次多把文件中多个数据都读进内容中,然后在一次写出去,这样的速度一定会比前面代码速度快。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* 字节流复制文件
* 采用数组缓冲提高效率
* 字节数组
* FileInputStream 读取字节数组
* FileOutputStream 写字节数组
*/
public class Copy_1 {
public static void main(String[] args) {
long s = System.currentTimeMillis();
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream("c:\\t.zip");
fos = new FileOutputStream("d:\\t.zip");
//定义字节数组,缓冲
byte[] bytes = new byte[1024*10];
//读取数组,写入数组
int len = 0 ;
while((len = fis.read(bytes))!=-1){
fos.write(bytes, 0, len);
}
}catch(IOException ex){
System.out.println(ex);
throw new RuntimeException("文件复制失败");
}finally{
try{
if(fos!=null)
fos.close();
}catch(IOException ex){
throw new RuntimeException("释放资源失败");
}finally{
try{
if(fis!=null)
fis.close();
}catch(IOException ex){
throw new RuntimeException("释放资源失败");
}
}
}
long e = System.currentTimeMillis();
System.out.println(e-s);
}
}
第二章 字符流
经过前面的学习,我们基本掌握的文件的读写操作,在操作过程中字节流可以操作所有数据,可是当我们操作的文件中有中文字符,并且需要对中文字符做出处理时怎么办呢?
2.1 字节流读取字符的问题
在读取含有中文的文件时,我们并没有看到具体的中文,而是看到一些数字,这是什么原因呢?既然看不到中文,那么我们如何对其中的中文做处理呢?要解决这个问题,我们必须研究下字符的编码过程。
2.2 字符编码表
我们知道计算机底层数据存储的都是二进制数据,而我们生活中的各种各样的数据,如何才能和计算机中存储的二进制数据对应起来呢?
这时美国人,就把每一个字符和一个整数对应起来,就形成了一张编码表,老美他们的编码表就是ASCII表。其中就是各种英文字符对应的编码。
编码表:其实就是生活中字符和计算机二进制的对应关系表。
1、ascii: 一个字节中的7位就可以表示。对应的字节都是正数。0-xxxxxxx
2、iso-8859-1:拉丁码表 latin,用了一个字节用的8位。1-xxxxxxx 负数。
3、GB2312:简体中文码表。包含6000-7000中文和符号。用两个字节表示。两个字节第一个字节是负数,第二个字节可能是正数
GBK:目前最常用的中文码表,2万的中文和符号。用两个字节表示,其中的一部分文字,第一个字节开头是1,第二字节开头是0
GB18030:最新的中文码表,目前还没有正式使用。
4. unicode:国际标准码表(万国编码表):无论是什么文字,都用两个字节存储。
- Java中的char类型用的就是这个码表。char c = 'a';占两个字节。
- Java中的字符串是按照系统默认码表来解析的。简体中文版 字符串默认的码表是GBK。
5、UTF-8:基于unicode,一个字节就可以存储数据,不要用两个字节存储,而且这个码表更加的标准化,在每一个字节头加入了编码信息(后期到api中查找)。
能识别中文的码表:GBK、UTF-8;正因为识别中文码表不唯一,涉及到了编码解码问题。
对于我们开发而言;常见的编码 GBK UTF-8 ISO-8859-1
文字--->(数字) :编码。 “abc”.getBytes() byte[]
(数字)--->文字 : 解码。 byte[] b={97,98,99} new String(b)
2.3 字符输入流Reader
上述程序中我们读取拥有中文的文件时,使用的字节流在读取,那么我们读取到的都是一个一个字节。只要把这些字节去查阅对应的编码表,就能够得到与之对应的字符。API中是否给我们已经提供了读取相应字符的功能流对象,Reader,读取字符流的抽象超类。
read():读取单个字符并返回
read(char[]):将数据读取到数组中,并返回读取的个数。
2.3.1 FileReader类
FileReader类只能用于读文本文件(用记事本打开,人能看懂的都叫文本文件)。
构造方法:
2.3.2 FileReader读取包含中文的文件
import java.io.FileReader;
import java.io.IOException;
/*
* 字符输入流读取文本文件,所有字符输入流的超类
* java.io.Reader
* 专门读取文本文件
*
* 读取的方法 : read()
* int read() 读取1个字符
* int read(char[] c) 读取字符数组
*
* Reader类是抽象类,找到子类对象 FileReader
*
* 构造方法: 绑定数据源
* 参数:
* File 类型对象
* String文件名
*/
public class ReaderDemo {
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader("c:\\1.txt");
/*int len = 0 ;
while((len = fr.read())!=-1){
System.out.print((char)len);
}*/
char[] ch = new char[1024];
int len = 0 ;
while((len = fr.read(ch))!=-1){
System.out.print(new String(ch,0,len));
}
fr.close();
}
}
2.4 字符输出流Writer
既然有专门用于读取字符的流对象,那么肯定也有写的字符流对象,Writer是写入字符流的抽象类。
字符流写的时候必须要刷新flush()。
2.4.1 FileWriter类
FileOutputStream 用于写入诸如图像数据之类的原始字节的流。要写入字符流,请考虑使用 FileWriter。
FileWriter只能用来写入文本文件。
此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。
2.4.2 FileWriter写入中文到文件中
写入字符到文件中,先进行流的刷新,再进行流的关闭
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
//演示FileWriter 用于操作文件的便捷类。
FileWriter fw = new FileWriter("d:\\text\\fw.txt");
fw.write("你好谢谢再见");//这些文字都要先编码。都写入到了流的缓冲区中。
fw.flush();
fw.close();
}
}
练习:
import java.io.FileWriter;
import java.io.IOException;
/*
* 字符输出流
* java.io.Writer 所有字符输出流的超类
* 写文件,写文本文件
*
* 写的方法 write
* write(int c) 写1个字符
* write(char[] c)写字符数组
* write(char[] c,int,int)字符数组一部分,开始索引,写几个
* write(String s) 写入字符串
*
* Writer类的子类对象 FileWriter
*
* 构造方法: 写入的数据目的
* File 类型对象
* String 文件名
*
* 字符输出流写数据的时候,必须要运行一个功能,刷新功能
* flush()
*/
public class WriterDemo {
public static void main(String[] args) throws IOException{
FileWriter fw = new FileWriter("c:\\1.txt");
//写1个字符
fw.write(100);
fw.flush();
//写1个字符数组
char[] c = {'a','b','c','d','e'};
fw.write(c);
fw.flush();
//写字符数组一部分
fw.write(c, 2, 2);
fw.flush();
//写如字符串
fw.write("hello");
fw.flush();
fw.close();
}
}
2.5 flush()和close()的区别?
flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用。
close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后在关闭流。流不可以使用。如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close完成刷新并关闭。
2.6 字符流练习
2.6.1 复制文本文件
思路:
1,既然是文本涉及编码表。需要用字符流。
2,操作的是文件。涉及硬盘。
3,有指定码表吗?没有,默认就行。
操作的是文件,使用的 默认码表。使用哪个字符流对象。直接使用字符流操作文件的便捷类。FileReader FileWriter
package day23.demo1;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
* 字符流复制文本文件,必须文本文件
* 字符流查询本机默认的编码表,简体中文GBK
* FileReader读取数据源
* FileWriter写入到数据目的
*/
public class Copy_2 {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try{
fr = new FileReader("c:\\1.txt");
fw = new FileWriter("d:\\1.txt");
char[] cbuf = new char[1024];
int len = 0 ;
while(( len = fr.read(cbuf))!=-1){
fw.write(cbuf, 0, len);
fw.flush();
}
}catch(IOException ex){
System.out.println(ex);
throw new RuntimeException("复制失败");
}finally{
try{
if(fw!=null)
fw.close();
}catch(IOException ex){
throw new RuntimeException("释放资源失败");
}finally{
try{
if(fr!=null)
fr.close();
}catch(IOException ex){
throw new RuntimeException("释放资源失败");
}
}
}
}
}
第三章 总结
字节流
字节输入流 InputStream 抽象类
-FileInputStream 操作文件的字节输入流
字节输出流 OuputStream抽象类
-FileOutputStream 操作文件的字节输出流
字符流
字符输入流 Reader抽象类(可读中文,writer时需要flush)
InputStreamReader 输入操作的转换流
FileReader 用来操作文件的字符输入流(简便的流)
字符输出流 Writer抽象类
OutputStreamWriter 输出操作的转换流
FileWriter 用来操作文件的字符输出流(简便的流)