字节流和字符流

对于文件必然有读和写的操作,读和写就对应了输入和输出流,流又分成字节和字符流。

1.从对文件的操作来讲,有读和写的操作——也就是输入和输出。

2.从流的流向来讲,有输入和输出之分。

3.从流的内容来讲,有字节和字符之分。

这篇文章先后讲解IO流中的字节流和字符流的输入和输出操作。

一、字节流

1)输入和输出流

首先,字节流要进行读和写,也就是输入和输出,所以它有两个抽象的父类InputStream、OutputStream。

  • InputStream抽象了应用程序读取数据的方式,即输入流。
  • OutputStream抽象了应用程序写出数据的方式,即输出流。

2)读写结束

在字节流中当读写结束,达到文件结尾时,称为EOF = End或者读到-1就读到结尾

3)输入流基本方法

首先我们要清楚输入流是什么。比如通过我们的键盘在文本文件上输入内容,这个过程键盘充当的就是输入流,而不是输出流。因为键盘的功能是将内容输入到系统,系统再写入到文件上。以下是输入流的基本方法read():

 int b = in.read(); //读取一个字节无符号填充到int低八位。-1是EOF。
 in.read(byte[] buf); //读取数据填充到字节数组buf中。返回的是读到的字节个数。
 in.read(byte[] buf,int start, int size)//读取数据到字节数组buf从buf的start位置开始存放size长度分数据

其中in是InputStream抽象类的实例,可以发现这个方法和RandomAccessFile类中的read()方法差不多,因为两者都是通过字节来读取的。

4)输出流基本方法

输出流是进行写的操作,其基本操作方法是write(),可以将此方法与输入read()方法一 一去对应,更好理解。

1 out.write(int b)//写出一个byte到流,b的低8位
2 out.write(byte[] buf)//将buf字节数组都写到流
3 out.write(byte[] buf, int start,int size) //字节数组buf从start位置开始写size长度的字节到流

 了解了InputStream、OutputStream的基本操作方法后,再来看看它们两个的“孩子”FileInputStream和FileOutputStream。

这两个子类具体实现了在文件上读取和写入数据的操作,日程编程中更多的是使用这两个类。

二、FileInputStream和FileOutputStream类的使用

-----------------FileInputStream类的使用

1.使用read()方法读取文件

 1 /**
 2      * 读取指定文件内容,按照16进制输出到控制台
 3      * 并且每输出10个byte换行
 4      * @throws FileNotFoundException 
 5      */
 6     public static void printHex(String fileName) throws IOException{
 7         //把文件作为字节流进行读操作
 8         FileInputStream in=new FileInputStream(fileName);
 9         int b;
10         int count=0;//计数读到的个数
11         while((b=in.read())!=-1){
12             if(b<=0xf){
13                 //单位数前面补0
14                 System.out.println("0");
15             }
16             System.out.print(Integer.toHexString(b& 0xff)+" ");
17             if(++count%10==0){
18                 System.out.println();
19             }
20         }
21         in.close();//一定要关闭流
22     }

运行结果(随便一个文件来测试的):

【Java IO流】字节流和字符流详解

注意:

  • FileInputStream()构造函数可以通过文件名(String)也可以通过File对象。上面的案例是使用文件名来构造的。
  • (b=in.read())!=-1 通过读到-1来判断是否读到文件结尾。
  • in.close() 使用完IO流的对象一定要关闭流,养成好习惯很重要。

2.使用read(byte[] buf,int start, int size)方法读取文件

上述方法只能一个一个字节读取,对于较大的文件效率太低,推荐使用这个方法来一次性读取文件。

 1 public static void printHexByBytes(String fileName) throws IOException{
 2         FileInputStream in=new FileInputStream(fileName);
 3         byte[] buf=new byte[20*1024];//开辟一个20k大小的字节数组
 4         /*
 5          * 从in中批量读取字节,放入到buf这个字节数组中
 6          * 从第0个位置开始放,最多放buf.length个
 7          * 返回的是读到的字节个数
 8          */
 9         //一次性读完的情况
10         int count=in.read(buf, 0, buf.length);
11         int j=1;
12         for(int i=0;i<count;i++){
13             if((buf[i]&0xff)<=0xf){
14                 //单位数前面补0
15                 System.out.print("0");
16             }
17             System.out.print(Integer.toHexString(buf[i]&0xff)+ " ");
18             if(j++%10==0){
19                 System.out.println();
20             }
21         }
22              in.close();
23     }
24 }

read(byte[] buf,int start, int size)返回的是读到的字节个数,即buf字节数组的有效长度,所以输出buf数组时用的长度是count而不是buf.length,因为我们不知道文件大小和数组大小的关系,上述方法适用于文件大小不超过数组大小的情况下,一次性把文件内容读取到数组里,这里就有一个问题了,如果文件大小超过数组大小,那又该如何读取才能把文件全部读完呢??

我们知道读到-1就是读到文件末,所以还是利用while循环重复读取直到读到-1结束循环,把上述代码修改后如下:

 1     public static void printHexByBytes(String fileName) throws IOException{
 2         FileInputStream in=new FileInputStream(fileName);
 3         byte[] buf=new byte[20*1024];//开辟一个20k大小的字节数组
 4         /*
 5          * 从in中批量读取字节,放入到buf这个字节数组中
 6          * 从第0个位置开始放,最多放buf.length个
 7          * 返回的是读到的字节个数
 8          */
 9         int j=1;
10         //一个字节数组读不完的情况,用while循环重复利用此数组直到读到文件末=-1
11         int b=0;
12         while((b=in.read(buf, 0, buf.length))!=-1){
13             for(int i=0;i<b;i++){
14                 if((buf[i]&0xff)<=0xf){
15                     //单位数前面补0
16                     System.out.print("0");
17                 }
18                 System.out.print(Integer.toHexString(buf[i]&0xff)+ " ");
19                 if(j++%10==0){
20                     System.out.println();
21                 }
22             }
23         }
24         in.close();
25     }
26 }

 好了,我们用一个大于数组的文件来测试一下结果(太长,只截图末尾):

【Java IO流】字节流和字符流详解

大家可以比较两者的不同,第二种优化后更适合日常的使用,因为无论文件大小我们都可以一次性直接读完。

-----------------FileOutputStream类的使用

FileOutputStream类和FileInputStream类的使用相类似,它实现了向文件中写出btye数据的方法。里面的一些细节跟FileInputStream差不多的我就不提了,大家自己可以理解的。

1.构造方法

 FileOutputStream类构造时根据不同的情况可以使用不同的方法构造,如:

1 //如果该文件不存在,则直接创建,如果存在,删除后创建
2         FileOutputStream out = new FileOutputStream("demo/new1.txt");//以路径名称构造
1 //如果该文件不存在,则直接创建,如果存在,在文件后追加内容
2         FileOutputStream out = new FileOutputStream("demo/new1.txt",true);

更多内容可以查询API。

2.使用write()方法写入文件

write()方法和read()相似,只能操作一个字节,即只能写入一个字节。例如:

1 out.wirte(‘A’);//写出了‘A’的低八位
2 int a=10;//wirte只能写八位,那么写一个int需要写4次,每次八位
3 out.write(a>>>24);
4 out.write(a>>>16);
5 out.write(a>>>8);
6 out.wirte(a);

每次只写一个字节,显然是不效率的,OutputStream当然跟InputStream一样可以直接对byte数组操作。

3.使用write(byte[] buf,int start, int size)方法写入文件

意义:把byte[]数组从start位置到size位置结束长度的字节写入到文件中。

语法格式和read相同,不多说明

三、FileInputStream和FileOutputStream结合案例

了解了InputStream和OutputStream的使用方法,这次结合两者来写一个复制文件的方法。

 1 public static void copyFile(File srcFile,File destFile)throws IOException{
 2         if(!srcFile.exists()){
 3             throw new IllegalArgumentException("文件:"+srcFile+"不存在");
 4         }
 5         if(!srcFile.isFile()){
 6             throw new IllegalArgumentException(srcFile+"不是一个文件");
 7         }
 8         FileInputStream in =new FileInputStream(srcFile);
 9         FileOutputStream out =new FileOutputStream(destFile);
10         
11         byte[] buf=new byte[8*1024];
12         int b;
13         while((b=in.read(buf, 0, buf.length))!=-1){
14             out.write(buf, 0, b);
15             out.flush();//最好加上
16         }
17         in.close();
18         out.close();
19     }

 测试文件案例:

try {
        IOUtil.copyFile(new File("C:\\Users\\acer\\workspace\\encode\\new4\\test1"), new File("C:\\Users\\acer\\workspace\\encode\\new4\\test2"));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    }

运行结果:

【Java IO流】字节流和字符流详解【Java IO流】字节流和字符流详解【Java IO流】字节流和字符流详解

复制成功!

四、DataInputStream和DataOutputStream的使用

 DataInputStream、DataOutputStream 是对“流”功能的扩展,可以更加方便地读取int,long。字符等类型的数据。

对于DataOutputStream而言,它多了一些方法,如

writeInt()/wirteDouble()/writeUTF()

 这些方法其本质都是通过write()方法来完成的,这些方法都是经过包装,方便我们的使用而来的。

 1.构造方法

以DataOutputStream为例,构造方法内的对象是OutputStream类型的对象,我们可以通过构造FileOutputStream对象来使用。

1 String file="demo/data.txt";
2         DataOutputStream dos= new DataOutputStream(new FileOutputStream(file));

2.write方法使用

1         dos.writeInt(10);
2         dos.writeInt(-10);
3         dos.writeLong(10l);
4         dos.writeDouble(10.0);
5         //采用utf-8编码写出
6         dos.writeUTF("中国");
7         //采用utf-16be(java编码格式)写出
8         dos.writeChars("中国");

3.read方法使用

以上述的写方法对立,看下面例子用来读出刚刚写的文件

 1 String file="demo/data.txt";
 2         IOUtil.printHex(file);
 3         DataInputStream dis=new DataInputStream(new FileInputStream(file));
 4         int i=dis.readInt();
 5         System.out.println(i);
 6         i=dis.readInt();
 7         System.out.println(i);
 8         long l=dis.readLong();
 9         System.out.println(l);
10         double d=dis.readDouble();
11         System.out.println(d);
12         String s= dis.readUTF();
13         System.out.println(s);
14         dis.close();

运行结果:

【Java IO流】字节流和字符流详解

总结:DataInputStream和DataOutputStream其实是对FileInputStream和FileOutputStream进行了包装,通过嵌套方便我们使用FileInputStream和FileOutputStream的读写操作,它们还有很多其他方法,大家可以查询API。

注意:进行读操作的时候如果类型不匹配会出错!

五、字节流的缓冲流BufferredInputStresam&BufferredOutputStresam

这两个流类为IO提供了带缓冲区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能。

从应用程序中把输入放入文件,相当于将一缸水倒入另一个缸中:

FileOutputStream---->write()方法相当于一滴一滴地把水“转移”过去

DataOutputStream---->write()XXX方法会方便一些,相当于一瓢一瓢地把水“转移”过去

BufferedOutputStream---->write方法更方便,相当于一瓢一瓢水先放入一个桶中(缓冲区),再从桶中倒入到一个缸中。提高了性能,推荐使用!

上述提到过用FileInputStream和FileOutputStream结合写的一个拷贝文件的案例,这次通过字节的缓冲流对上述案例进行修改,观察两者的区别和优劣。

主函数测试:

1 try {
2         long start=System.currentTimeMillis();
3         //IOUtil.copyFile(new File("C:\\Users\\acer\\Desktop\\学习路径.docx"), new File("C:\\Users\\acer\\Desktop\\复制文本.docx"));
4         long end=System.currentTimeMillis();
5         System.out.println(end-start);
6     } catch (IOException e) {
7         // TODO Auto-generated catch block
8         e.printStackTrace();
9     }

(1)单字节进行文件的拷贝,利用带缓冲的字节流

 1 /*
 2      * 单字节进行文件的拷贝,利用带缓冲的字节流
 3      */
 4     public static void copyFileByBuffer(File srcFile,File destFile)throws IOException{
 5         if(!srcFile.exists()){
 6             throw new IllegalArgumentException("文件:"+srcFile+"不存在");
 7         }
 8         if(!srcFile.isFile()){
 9             throw new IllegalArgumentException(srcFile+"不是一个文件");
10         }
11         BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFile));
12         BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFile));
13         int c;
14         while((c=bis.read())!=-1){
15             bos.write(c);
16             bos.flush();//刷新缓冲区
17         }
18         bis.close();
19         bos.close();
20     }

 运行结果(效率):【Java IO流】字节流和字符流详解

(2)单字节不带缓冲进行文件拷贝

 1 /*
 2      * 单字节不带缓冲进行文件拷贝
 3      */
 4     public static void copyFileByByte(File srcFile,File destFile)throws IOException{
 5         if(!srcFile.exists()){
 6             throw new IllegalArgumentException("文件:"+srcFile+"不存在");
 7         }
 8         if(!srcFile.isFile()){
 9             throw new IllegalArgumentException(srcFile+"不是一个文件");
10         }
11         FileInputStream in=new FileInputStream(srcFile);
12         FileOutputStream out=new FileOutputStream(destFile);
13         int c;
14         while((c=in.read())!=-1){
15             out.write(c);
16             out.flush();//不带缓冲,可加可不加
17         }
18         in.close();
19         out.close();
20     }

运行结果(效率):【Java IO流】字节流和字符流详解

(3)批量字节进行文件的拷贝,不带缓冲的字节流(就是上面第三点最初的案例的代码)

 1 /*
 2      * 字节批量拷贝文件,不带缓冲
 3      */
 4     public static void copyFile(File srcFile,File destFile)throws IOException{
 5         if(!srcFile.exists()){
 6             throw new IllegalArgumentException("文件:"+srcFile+"不存在");
 7         }
 8         if(!srcFile.isFile()){
 9             throw new IllegalArgumentException(srcFile+"不是一个文件");
10         }
11         FileInputStream in =new FileInputStream(srcFile);
12         FileOutputStream out =new FileOutputStream(destFile);
13         
14         byte[] buf=new byte[8*1024];
15         int b;
16         while((b=in.read(buf, 0, buf.length))!=-1){
17             out.write(buf, 0, b);
18             out.flush();//最好加上
19         }
20         in.close();
21         out.close();
22     }
View Code

相关文章:

  • 2021-10-02
  • 2021-06-22
  • 2021-12-01
  • 2022-03-07
  • 2022-12-23
  • 2022-12-23
  • 2018-11-02
  • 2022-12-23
猜你喜欢
  • 2021-09-24
  • 2021-07-22
  • 2021-12-02
  • 2022-02-03
  • 2022-12-23
  • 2021-09-04
相关资源
相似解决方案