3、IO--字节流和字符流
1、相关理论
在java.io包中的流操作主要有两大类:字节流类和字符流类
字节流:
在字节流中输出数据使用OutputStream类完成
使用InputStream类进行输入
字节流主要操作byte数据,以byte数组为准
主要的操作类OutputStream和InputStream类
字符流:
在字符流中输出主要是使用Writer类
输入主要使用Reader类完成
在程序中一个字符等于两个字节
java提供Reader和Writer两个专门操作字符流的类
Java中I/O操作相应步骤
以文件操作为例
操作流程如下:
1、使用类File打开一个文件
2、通过字节流或者字符流指定输出的位置
3、进行读/写操作
4、关闭输入/输出
2、使用字节流
2.1、字节输出流OutputStream
类OutputStream是整个I/O包中的字节输出流的最大父类
定义格式:
public abstract class java.io.OutputStream
implements java.io.Closeable, java.io.Flushable
可以看出OutputStream是一个抽象类
使用此类首先必须通过字类实例化对象
如果操作是一个文件可以使用FileOutputStram类
通过向上转型后可以实例化OutputStream
OutputStream中的主要操作方法:
close() 关闭输出流
flush() 刷新缓冲区
write(byte[] b) 将一个byte数组写入数据流
write(byte[] b,int off,int len) 将一个指定范围的byte数据写入数据流
write(int b) 将一个字节数据写入数据流
向文件中写入字符串实例
write(byte[] b)将字节数组全部数据写入到输出流中
write(byte b[])写入字节数组长度的字节到输出流中,也即是将字节数组中全部数据写入到输出流中。
其本质调用的是write(b, 0, b.length)方法,也即从offset未0,读取length长度的字节数组数据到输出流中。
这样一次性将数据写入到输出流中的方法比单个字节写入到输出流效率高。
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件,声明File对象 File file = new File("d:\\text.txt"); //2、通过子类实例化父类对象 OutputStream out = null;//准备好一个输出对象 out = new FileOutputStream(file);//通过对象多态性,进行实例化 //3、进行写操作 String str = "Hello word"; //只能输出byte数组,所以将字符串变为byte数组 byte b [] = str.getBytes(); out.write(b); //4、关闭输出流 out.close(); }
此时文件中的结果:
注意:多次执行的结果之后重写定义文件的内容
write(int b)把单个字节写入到输出流中
write(int b)方法用于把单个字节写入到输出流中。OutputStream的write(int b)
将一个包含了待写入数据的int变量作为参数写入。
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件,声明File对象 File file = new File("d:\\text.txt"); //2、通过子类实例化父类对象 OutputStream out = null;//准备好一个输出对象 out = new FileOutputStream(file,true);//通过对象多态性,进行实例化 //3、进行写操作 String str = "Hello word"; //只能输出byte数组,所以将字符串变为byte数组 byte b [] = str.getBytes(); // 通过write(int b)将数据写入到输出流,操作系统设备根据输出流处理到终端文件 for(byte bs : b){ out.write(bs); } //4、关闭输出流 out.close(); }
write(byte[] b,int off,int len)
-
b -- 目标字节数组。
-
off -- 将读取的数据写入到数组b中,off表示从数据b的哪个位置开始写。
-
len -- 要读取的字节数。
-
IOException -- 如果发生I/ O错误。
-
NullPointerException -- 如果b为 null.
-
IndexOutOfBoundsException -- 如果off为负,len为负,或len大于b.length - off。
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件,声明File对象 File file = new File("d:\\text.txt"); //2、通过子类实例化父类对象 OutputStream out = null;//准备好一个输出对象 out = new FileOutputStream(file,true);//通过对象多态性,进行实例化 //3、进行写操作 String str = "Hello word"; //只能输出byte数组,所以将字符串变为byte数组 byte b [] = str.getBytes(); int len = b.length; out.write(b, 0, len); //4、关闭输出流 out.close(); }
追加而不是更换
如果需要进行追加内容可以这样定义:
File file = new File("d:\\text.txt"); OutputStream out = null; out = new FileOutputStream(file,true);
此时使用另一个构造方法
public FileOutputStream(File file,boolean append) throws FileNotFoundException
此时的内容是在原来文件之后进行追加内容
flush()
flush()方法将所有写入到OutputStream的数据冲刷到相应的目标媒介中。
比如,如果输出流是FileOutputStream,那么写入到其中的数据可能并没有真正写入到磁盘中。
即使所有数据都写入到了FileOutputStream,这些数据还是有可能保留在内存的缓冲区中。
通过调用flush()方法,可以把缓冲区内的数据刷新到磁盘(或者网络,以及其他任何形式的目标媒介)中。
close()
当你结束数据写入时,需要关闭OutputStream。通过调用close()方法来关闭。
2.2、字节输入流InputStream
可以通过InputStream从文件中读内容
其类定义如下
public abstract class java.io.InputStream implements java.io.Closeable
本身也是一个抽象类
必须依靠子类
如果从文件中读取,子类肯定是FileInputStream
主要方法如下:
available() 可以取得输入文件的大小
close() 用于管不输入流
read() 读取内容,以数据的方式读取
read(byte[] b) 将内容读取到byte数组中,同时返回读取个数
read()方法
-1表示文件的结尾
读入的数据以int类型返回
public static void main(String[] args) throws IOException { //创建字节输入流 InputStream in = new FileInputStream("d:\\test.txt"); //读取文件的内容 //一次读取一个字节 //read() 得到的是一个数 int res = in.read(); while(res != -1){//-1表示文件的结尾 System.out.print((char)res); res = in.read(); }//关闭资源 in.close(); }
read(byte[] b)
public static void main(String[] args) throws IOException { //1、使用File找到文件 File file = new File("d:\\test.txt"); //2、通过对象多态进行实例化 InputStream in = new FileInputStream(file); //3、进行读操作 //所有读出的内容斗放在数组中 byte b[]= new byte[1024]; in.read(b); //4、关闭 in.close(); System.out.println(new String(b)); }
此时文件的内容已经读取出来
但是会出现很多的空格
此时的内容并不满足1024个字节(12个字节)
在将byte数组转换为字符串时,同时也将很多个无用的空间转为字符串
这样的操作是及其不合理的
指定大小的byte数组
public static void main(String[] args) throws IOException { //1、使用File找到文件 File file = new File("d:\\test.txt"); //2、通过对象多态进行实例化 InputStream in = new FileInputStream(file); //3、进行读操作 //所有读出的内容斗放在数组中 byte b[]= new byte[(int) file.length()]; in.read(b); //4、关闭 in.close(); System.out.println(new String(b)); }
根据上述的代码来看
虽然指定了byte数组的范围
但是程序开辟了很多的无用空间,这样很容易就造成了资源的浪费
此时的方法可以通过判断文件的字节数进行定义数组
通过循环读取内容
使用read()方法
public static void main(String[] args) throws IOException { //1、使用File找到文件 File file = new File("d:\\test.txt"); //2、通过对象多态进行实例化 InputStream in = new FileInputStream(file); //3、进行读操作 //所有读出的内容斗放在数组中 byte b[]= new byte[(int) file.length()]; for(int i=0;i<b.length;i++){ b[i]=(byte) in.read(); } in.close(); System.out.println(new String(b)); }
此时存在一个问题
如果此时不知道要输入的内容有多大
此时只能通过判断文件是否读到末尾的方式来读取文件
对上述代码进行修改:
public static void main(String[] args) throws IOException { //1、使用File找到文件 File file = new File("d:\\test.txt"); //2、通过对象多态进行实例化 InputStream in = new FileInputStream(file); //3、进行读操作 //所有读出的内容斗放在数组中 byte b[]= new byte[(int) file.length()]; int len =0; int temp=0; while((temp = in.read()) !=-1){ b[len] = (byte) temp; len++; } in.close(); System.out.println(new String(b)); }
程序代码中要判断代码temp接收到的内容是否为-1
正常情况下是不会返回-1
只有放输入流的内容已经读到底才会返回-1
2.3、使用输出流进行复制文件的内容
将a.txt文件中内容复制到aa.txt中
public static void main(String[] args) throws IOException { //输入流 查询 File file = new File("d:\\a.txt"); InputStream in = new FileInputStream(file); //输出流 添加内容 OutputStream out = new FileOutputStream("d:\\aa.txt"); byte [] buffer = new byte[(int) file.length()]; int len = 0; while((len = in.read(buffer)) != -1){ out.write(buffer, 0, len); } //关闭 out.close(); in.close(); }
使用输入流得到文件a.txt
使用输出流得到aa.txt
定义byte数组其长度问a.txt的长度
在使用read(byte [] b)进行读取a.txt中内容
使用write(byte [] b,int off,int len)进行对aa.txt文件写内容
3、使用字符流
3.1、字符输出流Writer
Writer本身是一个字符流的输出类
public abstract class java.io.Writer
implements java.lang.Appendable, java.io.Closeable, java.io.Flushable
本身也是一个抽象类
使用此类需要使用其字类
如果向文件中写入内容,应该使用FileWriter的字类
方法:
close() 关闭输出流
write(String str) 将字符串写入
write(char [] cbuf) 将字符串数组写入
flush() 强制性清空缓存
FileWriter构造方法如下:
public FileWriter(File file) throws IOException
类Writer除了实现Closeable、Flushable接口之外
还实现了Appendable接口
此接口表示内容可以被追加,接受的参数是CharSequence
实际上String类就实现了此接口
所以可以直接通过此接口的方法向输出流中追加内容
实例1:向文件中写入内容
write(String str)
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件 File file = new File("d:\\test.txt"); //2、通过子类实例化父类对象 Writer out = null; out = new FileWriter(file); //3、进行写操作 String str = "我是MrChengs\n \tI am"; out.write(str); //4、关闭资源 out.close(); }
整个程序和OutputStream的操作流程没什么太大的区别
唯一的好处是可以直接输入字符串
不用将字符串变为byte数组之后再输出
实例2:向文件中追加内容
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件 File file = new File("d:\\test.txt"); //2、通过子类实例化父类对象 Writer out = null; out = new FileWriter(file,true); //3、进行写操作 String str = "\r\nMrChengs"; out.write(str); //4、关闭资源 out.close(); }
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件 File file = new File("d:\\test.txt"); //2、通过子类实例化父类对象 Writer out = null; out = new FileWriter(file,true); //3、进行写操作 String str = "我是\r\nMrChengs"; //out.write(str); out.write(str, 0, str.length()); //4、关闭资源 out.close(); }
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件 File file = new File("d:\\test.txt"); //2、通过子类实例化父类对象 Writer out = null; out = new FileWriter(file,true); //3、进行写操作 String str = "我是\r\nMrChengs"; char[] cbuf = str.toCharArray(); out.write(cbuf, 0, cbuf.length); //4、关闭资源 out.close(); }
清除之前的内容重新运行两次得到如下的结果:
3.2、字节输入流
Reader类的定义格式
public abstract class java.io.Reader implements java.lang.Readable, java.io.Closeable {
相关方法:
close() 关闭资源
read() 读取单个字符。
read(char [] cbuf) 将字符读入数组。
read(char [] cbuf,int off,int len) 将字符读入数组的一部分。
read()
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件 File file = new File("d:\\test.txt"); //2、通过子类实例化父类对象 Reader reader = null; reader = new FileReader(file); //3、读取数据 int res =0; while((res = reader.read()) != -1){ System.out.print((char)res); } reader.close(); }
此时是一个读取单个字符
read(char [] cbuf)
将字符读入数组
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件 File file = new File("d:\\test.txt"); //2、通过子类实例化父类对象 Reader reader = null; reader = new FileReader(file); //3、读取数据 //创建字符数组用于存取读取的内容 char [] cbuf = new char[(int) file.length()]; reader.read(cbuf); System.out.print(new String(cbuf)); reader.close(); }
read(char [] cbuf,int off,int len)
将字符读入数组的一部分。
-
cbuf -- 目标位置
-
off -- 偏移量开始的存储字符
-
len -- 要获取的字符最大数量
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件 File file = new File("d:\\test.txt"); //2、通过子类实例化父类对象 Reader reader = null; reader = new FileReader(file); //3、读取数据 //创建字符数组用于存取读取的内容 char [] cbuf= new char[(int) file.length()]; reader.read(cbuf, 0, cbuf.length); System.out.print(new String(cbuf)); reader.close(); }
使用while循环将值逐一存放在char数组中:
public static void main(String[] args) throws IOException { //1、使用File类找到一个文件 File file = new File("d:\\test.txt"); //2、通过子类实例化父类对象 Reader reader = null; reader = new FileReader(file); //3、读取数据 char [] buffer = new char[(int) file.length()]; int len = 0; int temp = 0; while((temp = reader.read()) != -1){ buffer[len] = (char) temp; len++; } System.out.println(new String(buffer)); //4、关闭 reader.close(); }