IO概述
文件的读写操作:读文件是指把文件中的数据读取到内存中。反之,写文件是把内存中的数据写到文件中。通过流读写文件。
流:是指一连串流动的字符,是以先进先出的方式发送和接收数据的通道。
I(input):输入(读取)程序从文件中读数据,数据输入到内存。
O(output):输出(写入)程序向文件写入数据,从内存中输出数据。
数据源
数据源(Data Source),提供数据的原始媒介。常见的数据源有:数据库、文件、其他程序、内存、网络连接、IO设备。
数据源分为:源设备、目标设备。
-
源设备:为程序提供数据,一般对应输入流。
-
目标设备:程序数据的目的地,一般对应输出流。
流
流是一个抽象、动态的概念,是一连串连续动态的数据集合。
对于输入流而言,数据源就像水箱,流(Stream)就像水管中流动着的水流,程序就是我们最终的用户。我们通过流(A Stream)将数据源(Source)中的数据(information)输送到程序(Program)中。
对于输出流而言,目标数据源就是目的地(dest),我们通过流(A Stream)将程序(Program)中的数据(information)输送到目的数据源(dest)中。
按流向分:输出流:OutputStream和Writer作为基类。输入流:InputStream和Reader作为基类。
按照处理数据单元划分:
-
字节流:字节输入流InputStream作为基类、字节输出流OutputStream作为基类。
-
字符流:字符输入流Reader作为基类、字符输出流Writer作为基类。
字节流是8位通用字节流,其基本单位是字节。
字符流是16位Unicode字符流,这种流的基本单位是Unicode字符。这些流最适合用来处理字符串和文本,因为它们支持国际上大多数的字符集合语言。
按处理对象不同分类:
-
节点流:可以直接从数据源或目的地读写数据,如:FileInputStream、FileReader、DataInputStream等。
-
处理流:不直接连接到数据源或目的地,是“处理流的流”。通过对其他流的处理提高程序的性能,如 BufferedInputStream、BufferedReader等。处理流也叫包装流。
节点流处于IO操作的第一线,所有操作必须通过它们进行;处理流可以对节点流进行包装,提高性能或提高程序的灵活性。
字节流
读写的文件为二进制文件,如图片、视频、音频等,也可以读写文本文件,但可能出现乱码。所有文本文件用字符流更好。
字节输出流OutputStream
OutputStream此抽象类,是表示输出字节流的所有类的超类。操作的数据都是字节,定义了输出字节流的基本共性功能方法。
输出流中定义都是write()方法
FileOutputStream
FileOutputStream 用于写入诸如图像数据之类的原始字节的流。
OutputStream有很多子类,其中子类FileOutputStream可用来写入数据到文件。没有缓冲池,不存在刷新的问题,即flush()。
FileOutputStream类,即文件输出流,是用于将数据写入 File的输出流。
java.io.FileOutputStream
继承自java.io.OutputStream
继承自java.lang.Object
。
构造方法:
-
FileOutputStream(File file):创建一个向指定File对象表示的文件中写入数据的文件输出流。
-
FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流 。
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
字节输出(写入)流:操作的都是字节
OutputStream(抽象类)
FileOutputStream:专门用来向文件中写字节
需求:向文件中写字节
固定步骤:
1.输出流关联文件
2.利用输出流向文件中写字节
3.关流释放资源
构造方法:
FileOutputStream(String name):
可以不用创建文件,当我们指定文件路径的时候可以自动帮我们创建
如果根据制定的路径无法生成这个文件报错:FileNotFoundException
如果事先创建了文件,文件中有内容,那么利用FileOutputStream关联文件的时候,
会清空文件内容
成员方法:
void write(int b):向文件中写入一个字节
void write(byte[] b):将字节数组中的每个字节按照次序写入到文件中
void write(byte[] b, int off, int len):将字节数组中的一部分写入文件
*/
public class Demo {
public static void main(String[] args) throws IOException {
//method01();
//method02();
method03();
//method04();
}
private static void method01() throws FileNotFoundException, IOException {
//1.输出流关联文件
FileOutputStream fos = new FileOutputStream("1.txt");//默认覆盖模式
//2.利用输出流的方法向文件中写字节
fos.write("中国".getBytes());//把"中国"转换为字节写入到1.txt
//3.关流释放资源
fos.close();
}
private static void method02() throws FileNotFoundException, IOException {
//1.输出流关联文件
FileOutputStream fos = new FileOutputStream("2.txt");
//2.利用输出流的方法向文件中写字节
fos.write(49);//把49这个字节写入到2.txt,最终49字节在文件中会查找ASCII码表显示 1
fos.write(48);//把48这个字节写入到2.txt,最终48字节在文件中会查找ASCII码表显示 0
fos.write(48);//把48这个字节写入到2.txt,最终48字节在文件中会查找ASCII码表显示 0
//3.关流释放资源
fos.close();
}
private static void method03() throws FileNotFoundException, IOException {
//1.用流关联文件
FileOutputStream fos = new FileOutputStream("3.txt");
//2.向文件写入内容
/* byte[] bArr={97,98,99};
fos.write(bArr);*/
byte[] bArr = "abc".getBytes();//{97,98,99}
fos.write(bArr);
//3.关流释放资源
fos.close();
}
private static void method04() throws FileNotFoundException, IOException {
//1.用流关联文件
FileOutputStream fos = new FileOutputStream("3.txt");
//2.向文件写入内容
byte[] bArr = {65, 66, 67, 68};//A,B,C,D
fos.write(bArr, 1, 2);
//3.关流释放资源
fos.close();
}
}
字节输入流InputStream
把内存中的数据读到内存中,我们通过InputStream可以实现。InputStream此抽象类,是表示字节输入流的所有类的超类。,定义了字节输入流的基本共性功能方法。
-
int read():读取一个字节并返回,没有字节返回-1。
-
int read(byte[]):读取一定量的字节数,并存储到字节数组中,返回读取到的字节数。
FileInputStream类
InputStream有很多子类,其中子类FileInputStream可用来读取文件内容。
FileInputStream 从文件系统中的某个文件中获得输入字节。
java.io.FileInputStream
继承自java.io.InputStream
继承自java.io.Object
。
构造方法
//通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file指定
FileInputStream(File file)
//通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径名name指定
FileInputStream(String name)
读取单个字节read()
在读取文件中的数据时,调用read方法,实现从文件中读取数据。
abstract int read()//从输入流中读取数据的下一个字节
从文件中读取数据:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
字节输入(读取)流:InputStream(抽象类)
FileInputStream:读取文件中的字节
构造函数:
FileInputStream(String name):不会清空文件内容,如果被读取的文件不存在,直接报错
成员方法:
int read():从文件中读取一个字节,并且返回这个字节,每次通过同一个输入流对象调用read()方法
会按照顺序读取文件中的字节,当读到文件末尾的时候,read()方法返回-1
int read(byte[] b):尽可能的往指定的字节数组中装字节,返回每次读到的有效字节个数
操作步骤:
1.先用输入流关联文件
2.利用输入流中的方法读取文件中的字节
3.关流释放资源
*/
public class Demo {
public static void main(String[] args) throws IOException {
//method01();
//method02();
}
private static void method02() throws FileNotFoundException, IOException {
//1.先用输入流关联文件
FileInputStream fis = new FileInputStream("1.txt");//存有abc
//2.利用输入流中的方法读取文件中的字节
//循环读出文件中所有的字节
int b;
while ((b = fis.read()) != -1) {
// System.out.println(b);//第一次循环 fis.read()返回 97 97!=-1 打印97
//第二次循环 fis.read()返回 98 98!=-1 打印98
//第三次循环 fis.read()返回 99 99!=-1 打印99
System.out.println((char) b);
}
//3.关流释放资源
fis.close();
}
private static void method01() throws FileNotFoundException, IOException {
//1.先用输入流关联文件
FileInputStream fis = new FileInputStream("1.txt");
//2.利用输入流中的方法读取文件中的字节
int b = fis.read();//读取的是d对应的ASCII码
System.out.println(b);//100
System.out.println((char) b);//将100转成对应的字符'd'
//3.关流释放资源
fis.close();
}
}
读取字节数组read(byte[])
在读取文件中的数据时,调用read方法,每次只能读取一个,太麻烦了,于是我们可以定义数组作为临时的存储容器,这时可以调用重载的read方法,一次可以读取多个字节。
read(byte[] b):从输出流中读取一定数量的字节,并将其存储在缓冲区数组b中。返回值为int类型。
public class Demo {
public static void main(String[] args) throws IOException {
//method01();
//method02();
FileInputStream fis = new FileInputStream("5.txt");//存有abcde
//(当文件不大时,可以使用 fis.available()直接一次读完)
//byte[] bArr=new byte[fis.available()];
byte[] bArr = new byte[2];
int len;
while ((len = fis.read(bArr)) != -1) {
System.out.println(new String(bArr, 0, len));
//第一次循环bArr={97,98}
//new String(bArr,0,2)=>"ab"
//第二次循环bArr={99,100}
//new String(bArr,0,2)=>"cd"
//第三次循环bArr={101,100}
//new String(bArr,0,1)=>"e"
System.out.println(len);
}
fis.close();
}
}
字节流复制文件
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*文件拷贝
FileInputStream read()方法去读
FileOutputStream write()方法去写
操作步骤:
1.用流关联文件
2.进行读写操作
3.关流释放资源
*/
public class Demo {
public static void main(String[] args) throws IOException {
//1.用流关联文件,既有输入流关联还有输出流关联
FileInputStream fis = new FileInputStream("5.txt");
FileOutputStream fos = new FileOutputStream("copy.txt");
//2.从5.txt中读一个字节,再把这个字节写入到copy.txt
//5.txt abcde
//copy.txt abcde
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
//3.关流释放资源,先开后关
fos.close();
fis.close();
//一次读一个字节写一个字节效率太低
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*读多个字节和写多个字节来完成拷贝
* 操作步骤:
* 1.用流关联文件
* 2.进行读写操作
* 3.关流释放资源
*/
public class Demo {
public static void main(String[] args)throws IOException {
//1.用流关联文件
FileInputStream fis=new FileInputStream("abc.png");
FileOutputStream fos=new FileOutputStream("copy.png");
//2.利用字节数组拷贝,提高效率
byte[] b=new byte[1024];//一般开辟1024整数倍
int len;
while((len=fis.read(b))!=-1){
fos.write(b,0,len);//读到几个写几个
}
//3.关流释放资源,先开后关
fos.close();
fis.close();
}
}
字符流
在操作过程中字节流可以操作所有数据,可是当我们操作的文件中有中文字符,并且需要对中文字符做出处理时用字符流。读写的文件为文本文件,即使用记事本阅读没有乱码的文件。
字符编码表
我们知道计算机底层数据存储的都是二进制数据,而我们生活中的各种各样的数据,如何才能和计算机中存储的二进制数据对应起来呢?
这时老美他们就把每一个字符和一个整数对应起来,就形成了一张编码表,老美他们的编码表就是ASCII表。其中就是各种英文字符对应的编码。
编码表:其实就是生活中字符和计算机二进制的对应关系表。
1、ascii: 一个字节中的7位就可以表示。对应的字节都是正数。0-xxxxxxx。
2、iso-8859-1:拉丁码表 latin,用了一个字节用的8位。1-xxxxxxx 负数。
3、GB2312:简体中文码表。包含6000-7000中文和符号。用两个字节表示。两个字节第一个字节是负数,第二个字节可能是正数
4、GBK:目前最常用的中文码表,2万的中文和符号。用两个字节表示,其中的一部分文字,第一个字节开头是1,第二字节开头是0。Java中的字符串是按照系统默认码表来解析的。简体中文版 字符串默认的码表是GBK。
5、unicode(UTF-16):国际标准码表:无论是什么文字,都用两个字节存储。 Java中的char类型用的就是这个码表。char c = ‘a’;占两个字节。
6、UTF-8:基于unicode,一个字节就可以存储数据,不要用两个字节存储,而且这个码表更加的标准化,在每一个字节头加入了编码信息(后期到api中查找)。
能识别中文的码表:GBK、UTF-8;正因为识别中文码表不唯一,涉及到了编码解码问题。
对于我们开发而言;常见的编码有GBK、UTF-8、ISO-8859-1
文字→(数字):编码。 “abc”.getBytes() byte[]
(数字)→文字:解码。 byte[] b={97,98,99} new String(b)
字符输出流Writer
Writer是写入字符流的抽象类。其中描述了相应的写的动作。
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)//写入字符串的某一部分
FileWriter类
FileWriter是用于写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。
构造方法:
FileWriter(File file):根据给定的File对象构造一个FileWriter对象
FileWriter(File file,boolean append):根据给定的File对象构造一个FileWriter对象
FileWriter(String fileName):根据给定的文件名构造一个FileWriter对象
FileWriter(String fileName,boolean append):根据给定的文件名以及指示是否附加写入数据的boolean值来构造FileWriter对象
FileWriter fw=new FileWriter("b.txt",true);//在原有的b.txt文件中追加数据。
//false表示覆盖,为默认模式
FileWriter写入中文到文件中
写入字符到文件中,先进行流的刷新,再进行流的关闭。
import java.io.FileWriter;
import java.io.IOException;
/*
* 字符输出(输出)流
* Writer(抽象类)
* FileWriter
* 构造方法:
* FileWriter(String fileName) :如果文件没有,会自动建一个空白文件
* 如果文件已存在,就将原来的文件内容清空
* 成员方法:
* void write(int c)
* void write(String str)
* void write(char[] cbuf)
void write(char[] cbuf, int off, int len)
void flush():刷新方法,将字符数组(缓冲区)数据刷新到文件中
操作步骤:
1.使用字符输出流关联文件
2.利用字符输出流的方法向文件中写内容
3.关流释放资源
*/
public class Demo {
public static void main(String[] args) throws IOException {
//method01();//"b.txt"中内容为:a
//method02();//"b.txt"中内容为:你a好
//method03();//"b.txt"中内容为:你ab
//method04();//"b.txt"中内容为:ab
}
private static void method01() throws IOException {
FileWriter fw=new FileWriter("b.txt");
fw.write(97);//97-->a码值
fw.write('a');//写一个'a'会写到FileWriter底层的字符数组中去,并没有直接写到文件中
//fw.flush();//把底层字符数组中存储的'a'刷新到b.txt
fw.close();//close()方法先刷新然后会关流释放资源
}
private static void method02() throws IOException {
FileWriter fw=new FileWriter("b.txt");
fw.write("你a好");
fw.close();
}
private static void method03() throws IOException {
FileWriter fw=new FileWriter("b.txt");
char[] chArr={'你','a','b'};
fw.write(chArr);
fw.close();
}
private static void method04() throws IOException {
FileWriter fw=new FileWriter("b.txt");
char[] chArr={'你','a','b'};
fw.write(chArr, 1, 2);
fw.close();
//"b.txt"中内容为:ab
}
}
字符输入流Reader
为了简化字节流操作字符的繁琐。API中是否给我们已经提供了读取相应字符的功能流对象,Reader,读取字符流的抽象超类。
-
read():读取单个字符并返回。返回类型为int。
-
read(char[]):将数据读取到数组中,并返回读取的个数。返回类型为int。
FileReader类
FileReader是用来读取字符文件的便捷类。此类的构造方法假定平台默认字符码表(中文操作系统默认GBK) 。
构造方法:
-
FileReader(File file):在给定从中读取数据的File的情况下创建一个新FileReader。
-
FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新FileReader。
成员方法:
-
read():读取单个字符。返回值类型为int。
-
read(char[] cbuf):将字符读入数组。返回值类型为int。
FileReader读取包含中文的文件
import java.io.FileReader;
import java.io.IOException;
/*字符输入(读取)流
* Reader(抽象类)
* FileReader
* 构造方法:
* FileReader(String name)
*
* 成员方法:
* int read() :每次调用读取一个字符,读不到数据时返回-1
int read(char[] cbuf):每次将字符读到字符数组中,返回读取到字符个数
操作步骤:
1.利用字符输入流关联文件
2.利用字符输入流的方法读文件内容
3.关流释放资源
*/
public class Demo{
public static void main(String[] args) throws IOException {
//method01();
//method02();
}
private static void method01() throws IOException {
// 1.利用字符输入流关联文件
//读取文件时,必须保证所读取的文件实际存在
FileReader fr=new FileReader("3.txt");
//2.利用字符流的read()方法读单个字符
int ch;
while((ch=fr.read())!=-1){
System.out.println((char)ch);
}
//3.关流释放资源
fr.close();
}
private static void method02() throws IOException {
// 1.利用字符输入流关联文件
//读取文件时,必须保证所读取的文件实际存在
FileReader fr = new FileReader("3.txt");
//2.利用字符数组读取
char[] chArr = new char[1024];
int len;
while ((len = fr.read(chArr)) != -1) {
//String(char[] value, int offset, int count)
System.out.println(new String(chArr, 0, len));//第一次循环:chArr={'你','a'}
System.out.println(len);
}
}
}
flush()和close()的区别
-
flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用。适用场景:一次性输出数据量比较大,边输出边刷新。
-
close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后在关闭流。流不可以使用。如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close完成刷新并关闭。
复制文本文件
练习:复制文本文件。
思路:
1、既然是文本涉及编码表。需要用字符流。
2、操作的是文件。涉及硬盘。
3、有指定码表吗?没有,默认就行。
操作的是文件,使用的 默认码表。使用哪个字符流对象。直接使用字符流操作文件的便捷类。FileReader FileWriter。
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//利用字符输出流和字符输入流文件拷贝
/*
* 方法一、读一个字符,写一个字符
* 方法二、读一个字符数组,写一个字符数组
*
* 字符流的拷贝不能拷贝图片,图片中都是纯字节数据,不涉及码表转换
* 拷贝图片,音频,视频,不涉及文字的文件都只能用字节流
*/
public class Demo {
public static void main(String[] args)throws IOException {
//method01();
//method02();
}
private static void method01() throws FileNotFoundException, IOException {
//1.用字符输入流关联要读取内容的文件,用字符输出流关联要写入的文件
FileReader fr=new FileReader("c.txt");
FileWriter fw=new FileWriter("copy2.txt");
//2.从c.txt中读一个字符,将这个字符写入到copy2.txt
//读一个写一个
int ch;
//c.txt 你好ab
//copy2.txt
while((ch=fr.read())!=-1){
fw.write(ch);
}
//3.关流释放资源,先开后关
fw.close();
fr.close();
}
private static void method02() throws FileNotFoundException, IOException {
//1.用字符输入流关联要读取内容的文件,用字符输出流关联要写入的文件
FileReader fr=new FileReader("c.txt");
FileWriter fw=new FileWriter("copy2.txt");
//2.定义一个字符数组
char[] chArr=new char[1024];
int len;
while((len=fr.read(chArr))!=-1){
fw.write(chArr, 0, len);
}
//3.关流释放资源,先开后关
fw.close();
fr.close();
}
}
评论区