关于Java中<字节流>和<字符流>的操作
在程序中所有的数据都是以流的方式进行传输或者保存的,程序需要数据的时候需要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流。
可以通过下面的输入输出流关系图表示这种方式。
在java.io包中流的操作主要有字节流、字符流两大类,并且两个都具备输入输出的操作。
在字节流中输出数据主要使用OutputStream类完成,输入则是InputStream类。
在字符流中输出数据主要使用Writer类完成,输入则是Reader类。
任何一种流接口,它下面都会有多个实现类。下图展示的是流之间的层次关系图。
字节流:
字节流主要操作byte类型数据,以byte数组为准,主要操作类是OutputStream类和InputStream类。
1)字节输出流:OutputStreame
OutputStream是整个IO包中字节输出流最大的父类,此类的定义如下:
public abstract class OutputStream extends Object implements Closeable, Flushable
从这个定义中可以发现,OutputStream类是一个抽象类,如若要使用此类,首先就必须要通过子类来实例化对象。假设现在要操作的事一个文件,则可以使用FileOutputStream类,通过向上转型后,可以为OutputStream实例化,在OutputStream类中的主要操作方法如下:
1 public void close() throws IOException //关闭输出流 2 public void flush() throws IOException //刷新缓冲区 3 public void write(byte[] b) throws IOException //将一个byte数组写入数据流 4 public void write(byte[] b,int off,int len) throws IOException //将一个指定范围的byte数组写入数据流 5 public abstract void write(int b) throws IOException //将一个字节数据写入数据流
此时会用FileOutputStream子类,此类的构造方法如下:
public FileOutputStream(File file) throws FileNotFoundException
操作时必须接收File类实例,指明要输出的文件路径。
在OutputStream类的定义中可以发现此类实现了Closeable和Flushable两个接口,这两个接口的作用从其定义方法中可以得知,Closeable表示可关闭,Flushable表示可刷新,而且在OutputStream类中已经有了这两个方法的实现,所以操作时一般不会关心这两个接口,而直接使用OutputStream类就行了。下面举一个例子:
1 import java.io.*; 2 3 /** 4 * Author : Rabbit Yulin 5 * Created on 2017/2/24 0024 上午 11:10 6 * Description : 向文件中写入字符串 7 */ 8 public class OutputStreamDemo1 { 9 public static void main(String[] args) throws IOException { 10 /*使用File指定一个文件*/ 11 File f = new File("d:"+File.pathSeparator+"test.txt");//申明File对象 12 /*通过子类实例化父类*/ 13 OutputStream out; //输出对象 14 out = new FileOutputStream(f); //通过对象多态性进行实例化 15 /*写入操作*/ 16 String str = "Hello , World"; //字符串 17 byte b[] = str.getBytes(); //因为只能输出byte数组,所以将字符串变为byte数组 18 out.write(b); //将内容输出 19 /*关闭输出流*/ 20 out.close(); 21 } 22 }
程序运行后可以发现D盘下面已经新增了一个test.txt的文件,打开查看的时候妥妥的内容是:“Hello , World”。上面的代码在实例化、写、关闭的时候都会有异常,为了方便起见所以在主方法上直接throws了IOException抛出异常,这样可以减少try…catch语句。
可以很清楚的知道一个问题,那就是test.txt文件在程序操作之前是不存在的(当然如果硬是说“我创建了一个”那也没办法,反正会被覆盖掉),程序会自动创建这个文件,并且将内容写入到这个文件之中。将一个字符串变为byte的数组之后,然后使用write(byte[] b)的方式将byte数组直接写入到文件中,当然也可以通过循环把每一个字节一个一个地写入到文件中(其实并没有什么不同,实现的功能都是一样的,可以根据喜好随意),同样的,下面放出一个Demo:
1 import java.io.*; 2 /** 3 * Author : Rabbit Yulin 4 * Created on 2017/2/24 0024 上午 11:29 5 * Description : 6 */ 7 public class OutputStreamDemo2 { 8 public static void main(String[] args) throws IOException { 9 /*File指定一个文件*/ 10 File f = new File("d:"+File.pathSeparator+"test2.txt"); //申明对象 11 /*子类实例化父类*/ 12 OutputStream out; //输出对象 13 out = new FileOutputStream(f); //实例化 14 /*写操作*/ 15 String str = "Hello , Again"; 16 byte b[] = str.getBytes(); 17 for(int i = 0;i<b.length;i++){ 18 out.write(b[i]); 19 } 20 /*关闭输出流*/ 21 out.close(); 22 } 23 }
2)追加内容
刚才有提到过,假若在指定的目录之下已经有一个同名文件,在程序运行的时候该文件会被覆盖。一样的,如果再直接往文本中写入内容,会造成原有数据的覆盖。那么此时就可以通过FileOutputStream向文件中追加内容,FileOutputStream的另外一个构造方法:
public FileOutputStream(File file,boolean append) throws FileNotFoundException
在该构造方法中,如果将append的值设置为true,则表示在文件的末尾追加内容。如果要在文件之后的追加内容,是紧跟在原有的内容之后增加换行,使文件内容显示更加清晰,则可以使用"\r\n"换行。
3)字节输入流 InputStream
既然程序可以向文件写入内容,则可以通过InputStream从文件中把数据读取进程序当中。InputStream类的定义如下:
public abstract class InputStream extends Object implements Closeable
与OutputStream类一样,InputStream本身也是一个抽象类,必须依靠子类。如果现在从文件中读取,子类肯定是FileInputStream。InputStream类中的主要方法如下:
public int availabel() throws IOException //可以取得输入文件的大小 public void close() throws IOException //关闭输入流 public abstract int read() throws IOException //读取内容,以数字方式读取 public int read(byte[] b) throws IOException //将内容读到byte数组中同时返回读入的个数
通过一个例子来看看如何从文件中读取内容:
import java.io.*; /** * Author : Rabbit Yulin * Created on 2017/2/24 0024 下午 1:46 * Description : */ public class InputStreamDemo1 { public static void main(String[] args) throws IOException { /*File指定一个文件*/ File f = new File("d:"+File.pathSeparator+"test.txt"); //申明File文件 /*子类实例化父类*/ InputStream is; //准备一个输入对象 is = new FileInputStream(f); //通过对象多态性进行实例化 /*进行读操作*/ byte[] b = new byte[1024]; //所有的内容读到次数组中 is.read(b);/*关闭输入流*/ is.close(); System.out.println("内容为:"+new String(b)); //把数组变为字符串 } }
虽然内容都被读取进来了,但是有一个问题就是打印结果的后面有很多个空格。这是因为byte数组的大小有1024,然而实际的内容只有13个字节,也就是说还存在着1011个空白的弓箭,在将byte数组变为字符串时也将这1011个无用的空间转为了字符串,这样的操作肯定是不合理的。如果要想要解决以上的问题,则要观察read的方法,在次方法上有一个返回值,此返回值表示想数组中写入了多少个数据。因此把上面的程序修改一下的代码如下:
1 import java.io.*; 2 3 /** 4 * Author : Rabbit Yulin 5 * Created on 2017/2/24 0024 下午 1:46 6 * Description : 7 */ 8 public class InputStreamDemo1 { 9 public static void main(String[] args) throws IOException { 10 /*File指定一个文件*/ 11 File f = new File("d:"+File.pathSeparator+"test.txt"); //申明File文件 12 /*子类实例化父类*/ 13 InputStream is; //准备一个输入对象 14 is = new FileInputStream(f); //通过对象多态性进行实例化 15 /*进行读操作*/ 16 byte[] b = new byte[1024]; //所有的内容读到次数组中 17 int len = is.read(b); 18 /*关闭输入流*/ 19 is.close(); 20 System.out.println("读取的数据长度:"+len); 21 System.out.println("内容为:"+new String(b,0,len)); //把数组变为字符串 22 } 23 }
这样处理之后的代码再次运行,发现那些多余的空格就不会再产生了。因为程序在最后只是将byte数组指定范围中的内容编程了字符串。需要注意的一点是,FileInputStream读取时如果指定的路径不存在,则程序会出现异常。
以上的这个问题解决的方式似乎还是有些不太完美,因为虽然最后指定了byte数组的范围,但是程序依然开辟了很多的无用空间,这样肯定会造成资源的浪费,那么此时能否根据根据文件的数据长度来开辟空间大小?查看了API可以得知File类中存在一个length()方法,此方法可以取得文件的大小,来开辟指定的byte数组空间。
1 import java.io.*; 2 3 /** 4 * Author : Rabbit Yulin 5 * Created on 2017/2/24 0024 下午 2:37 6 * Description : 7 */ 8 public class InputStreamDemo2 { 9 public static void main(String[] args) throws IOException { 10 File f = new File("d:"+ File.pathSeparator+"test.txt"); 11 InputStream is; 12 is = new FileInputStream(f); 13 byte[] b = new byte[(int)f.length()]; 14 is.read(b); 15 is.close(); 16 System.out.println("内容为:"+new String(b)); 17 } 18 }
这种读取的方式其实适用范围比较小,因为文本数据只要很大,那就造成一些未知的错误了。
字符流:
在程序中一个字符等于两个字节,那么Java提供了Reader和Writer两个专门操作字符流的类。
1)字符输出流 Writer
Writer 本身是一个字符流的输出类,此类的定义如下:
public abstract calss Writer extends Object implements Appendable,Closeable,Flushable;
此类也是一个抽象类,如果要使用这个类则需要使用其子类,这个时候如果要往文件中写入数据,应该使用FileWriter的子类。Writer类的常用方法如下:
public abstract void close() throws IOException //关闭输出流 public void write(String str) throws IOException //将字符串输出 public void write(char[] cbuf) throws IOException //将字符数组输出 public abstract void flush() throws IOException //强制性清空缓存
FileWriter类的构造方法定义如下:
public FileWriter(File file) throws IOException
在Writer类中除了实现Closeable和Flushable两个接口之外,还实现一个Appendable接口,此接口表示的是内容可以被追加,接受的参数是CharSequence,实际上String类就实现了此接口,所以可以直接通过次接口的方法向输出流中追加内容。例如:
1 import java.io.File; 2 import java.io.FileWriter; 3 import java.io.IOException; 4 import java.io.Writer; 5 6 /** 7 * Author : Rabbit Yulin 8 * Created on 2017/2/24 0024 下午 3:31 9 * Description : 10 */ 11 public class WriterDemo1 { 12 public static void main(String[] args) throws IOException { 13 File f = new File("d:"+File.pathSeparator+"test.txt"); 14 Writer out; 15 out = new FileWriter(f); 16 String str = "Hello,World"; 17 out.write(str); 18 out.flush(); 19 out.close(); 20 } 21 }
整个代码看下来和OutputStream没有太大的区别,唯一的好处就是可以直接输出字符串,而不用再将字符串变为byte数组之后再输出。
2)使用FileWriter追加文件的内容
在使用字符流操作的时候,也可以实现文件的追加功能,直接使用FileWriter类黄总的一下构造,就可以实现追加:
public FileWriter(File file,boolean append) throws IOException
将append的值设置为truw,表示追加。
3)字符输入流Reader
Reader是使用字符的方式从文件中取出数据,Reader类的定义如下:
public abstract class Reader extends Object implement Readable,Closeable
Reader不用说,也是一个抽象类,如果要在从文件中刦内容时,则可以直接使用FileReader子类。Reader类的常用方法:
1 public abstract void close() throws IOException //关闭输出流 2 public int read() throws IOException //读取单个字符 3 public int read(char[] cbuf) throws IOException //将内容读到字符数组内,返回读入的长度
FileReader的构造方法定义如下:
public FileReader(File file) throws FileNotFoundException
以下放出实例来读取文件中的文本内容
1 import java.io.*; 2 3 /** 4 * Author : Rabbit Yulin 5 * Created on 2017/2/24 0024 下午 3:51 6 * Description : 7 */ 8 public class ReaderDemo1 { 9 public static void main(String[] args) throws IOException { 10 File f = new File("d:"+File.pathSeparator+"test.txt"); 11 Reader reader = new FileReader(f); 12 char[] c = new char[1024]; 13 int len = reader.read(c); 14 reader.close(); 15 System.out.println("内容为:"+new String(c,0,len)); 16 } 17 }
如果此时不知道数据的长度,也可以使用循环的方式进行内容的读取:
1 import java.io.*; 2 3 /** 4 * Author : Rabbit Yulin 5 * Created on 2017/2/24 0024 下午 3:54 6 * Description : 7 */ 8 public class ReaderDemo2 { 9 public static void main(String[] args) throws IOException { 10 File f = new File("d:"+ File.pathSeparator+"test.txt"); 11 Reader reader = new FileReader(f); 12 int len = 0; 13 char[] c = new char[1024]; 14 int temp = 0; 15 while((temp = reader.read()) != -1){ 16 c[len] = (char)temp; 17 len++; 18 } 19 reader.close(); 20 System.out.println("内容为:"+ new String(c,0,len)); 21 } 22 }