• 黑马程序员——Java基础---IO(二)---IO字节流、流操作规律


    ------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

      字节流

    一、概述

    1、字节流和字符流的基本操作是相同的,但字节流还可以操作其他媒体文件。

    2、由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。

    3、读写字节流:InputStream   输入流(读)

                                 OutputStream  输出流(写)

    4、为何不用进行刷流动作:

            因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。所以可直接将字节数据写入到指定文件中。

    5、InputStream特有方法:

            int available();//返回文件中的字节个数

    注:可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法慎用,当文件不大时,可以使用。

    示例

     1 /*
     2 复制一个图片
     3 思路:
     4 1,用字节读取流对象和图片关联。
     5 2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
     6 3,通过循环读写,完成数据的存储。
     7 4,关闭资源。
     8 
     9 */
    10 
    11 import java.io.*;
    12 class  CopyPic
    13 {
    14     public static void main(String[] args) 
    15     {
    16         FileOutputStream fos = null;//建立字节文件写入流
    17         FileInputStream fis = null;//建立字节文件读取流
    18         try
    19         {
    20             fos = new FileOutputStream("c:\1.jpg");//关联文件
    21             fis = new FileInputStream("1.jpg");//关联文件
    22 
    23             byte[] buf = new byte[1024];
    24 
    25             int len = 0;
    26 
    27             while((len=fis.read(buf))!=-1)//缓冲区提供的 一次读一行的方法,只要不读到末尾
    28             {
    29                 //fos.write(buf[0]);
    30                 fos.write(buf,0,len);
    31             }
    32         }
    33         catch (IOException e)
    34         {
    35             throw new RuntimeException("复制文件失败");
    36         }
    37         finally
    38         {
    39             try
    40             {
    41                 if(fis!=null)
    42                     fis.close();//读取流不为空,则关闭 读取流
    43             }
    44             catch (IOException e)
    45             {
    46                 throw new RuntimeException("读取关闭失败");
    47             }
    48             try
    49             {
    50                 if(fos!=null)
    51                     fos.close();//写入流不为空,则关闭写入流
    52             }
    53             catch (IOException e)
    54             {
    55                 throw new RuntimeException("写入关闭失败");
    56             }
    57         }
    58     }
    59 }

    二、字节流缓冲区

            同样是提高了字节流的读写效率。

    1、读写特点:

            read():会将字节byte型值提升为int型值

            write():会将int型强转为byte型,即保留二进制数的最后八位。

    2、原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。

            1)先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区。

            2)循环这个动作,直到最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素。

            3)每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增。

            4)取出的时候,数组中的元素在减少,取出一个,就减少一个,直到减到0即元素取完。

            5)当文件中的全部数据都被读取出时,read()方法就返回-1。

    3、自定义读取字节流缓冲区

            需求:根据字节流缓冲区的原理,自定义一个字节流缓冲区。

    注意:

            1、字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。

           因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。

           所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。而在写入数据时,只写该int类型数据的最低8位。

            2、byte类型的-1提升为int类型时还是-1。原因:因为在bit8个1前面补的全是1导致的。如果在bit8个1前面补0,即可以保留原字节数据不变,又可以避免-1的出现。这时将byte型数据&0xff即255即可。

    代码:

     1 /*
     2 演示MP3的复制,通过缓冲区
     3 BufferedOutputStream
     4 BufferedInputStream
     5 */
     6 import java.io.*;
     7 class MyBufferedInputStream
     8 {
     9     private InputStream in;
    10 
    11     private byte[] buf = new byte[1024*4];//自定义缓冲区  为字节数组
    12         
    13     private int pos = 0,count = 0;  //pos  确认数组的位置,方便存储,count确认输入的个数,
    14     
    15     MyBufferedInputStream(InputStream in)
    16     {
    17         this.in = in;
    18     }
    19 
    20     //一次读一个字节,从缓冲区(字节数组)获取。
    21     public int myRead()throws IOException//字节流返回值类型为int,一次读一个的方法读取硬盘上数据,并存储buf中。
    22     {
    23     
    24         if(count==0)
    25         {
    26             count = in.read(buf);//计数 键盘录入的个数
    27             if(count<0)            //当count为零时, 说明已存储完成
    28                 return -1;
    29             pos = 0;
    30             byte b = buf[pos];   //
    31 
    32             count--;
    33             pos++;
    34             return b&255;//返回的byte类型提升为int类型,字节数增加,且高24位被补1,原字节数据改变。  
    35                          //通过与上255,主动将byte类型提升为int类型,将高24位补0,原字节数据不变。  
    36                          //而在输出字节流写入数据时,只写该int类型数据的最低8位。  
    37         }
    38         else if(count>0)
    39         {
    40             byte b = buf[pos];
    41 
    42             count--;
    43             pos++;
    44             return b&0xff;//
    45         }
    46         return -1;
    47 
    48     }
    49     public void myClose()throws IOException
    50     {
    51         in.close();
    52     }
    53 }
    54 class CopyMp3
    55 {
    56     public static void main(String[] args)throws IOException
    57     {
    58         long start =System.currentTimeMillis();//获取时间的毫秒数
    59         copy_2();
    60         long end=System.currentTimeMillis();
    61         System.out.println((end-start)+"毫秒");//通过相减可以得出 用时
    62     }
    63 
    64 
    65     public static void copy_2()throws IOException  //自定义缓冲区完成复制
    66     {
    67         MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("1.mp3"));
    68         BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\1.mp3"));
    69         
    70         int by = 0;
    71 
    72         //System.out.println("第一个字节:"+bufis.myRead());
    73 
    74         while((by=bufis.myRead())!=-1)
    75         {
    76             bufos.write(by);
    77         }
    78 
    79         bufos.close();
    80         bufis.myClose();
    81     }
    82     
    83     public static void copy_1()throws IOException//通过字节流的缓冲区完成复制
    84     {
    85         BufferedInputStream bufis=new BufferedInputStream(new FileInputStream("1.mp3"));
    86         BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("2.mp3"));
    87 
    88         int by=0;
    89 
    90         while((by=bufis.read())!=-1)
    91         {
    92             bufos.write(by);
    93         }
    94         bufos.close();
    95         bufis.close();
    96 
    97     }
    98 }

                                                                    流操作规律

    一、键盘录入以及

    1、标准输入输出流

            System.in:对应的标准输入设备,键盘。

            Ssytem.out:对应的是标准的输出设备,控制台。

            System.in的类型是InputStream.

            System.out的类型是PrintStream是OutputStream的子类FilterOutputStream的子类。

    2、整行录入

           当使用输入流进行键盘录入时,只能一个字节一个字节进行录入。为了提高效率,可以自定义一个数组将一行字节进行存储。当一行录入完毕,再将一行数据进行显示。这种正行录入的方式,和字符流读一行数据的原理是一样的。也就是readLine方法。

          那么能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?readLine方法是字符流BufferedReader类中方法。而键盘录入的read方法是字节流InputStream的方法。

          那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?这就需要用到转换流了。

    3、转换流

    3.1 转换流的由来:

           a、字符流与字节流之间的桥梁

           b、方便了字符流与字节流之间的操作

    转换流的应用:

          字节流中的数据都是字符时,转成字符流操作更高效。

    3.2   InputStreamReader将字节流通向字符流

           a、获取键盘录入对象。

                  InputStream in=System.in;

           b、将字节流对象转成字符流对象,使用转换流。

                  InputStreamReaderisr=new InputStreamReader(in);

           c、为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader

                  BufferedReaderbr=new BufferedReader(isr);

           //键盘录入最常见写法

                  BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));

    3.3   OutputStreamWriter字符流通向字节流

           字符通向字节:录入的是字符,存到硬盘上的是字节。步骤和InputStreamReader转换流一样。

    示例:

      1 import java.io.*;
      2 
      3 /*
      4 1
      5 源:键盘录入
      6 目的:控制台
      7 
      8 2    需求:想把键盘录入的数据存储到一个文件中
      9 
     10 源:    键盘
     11 目的:  文件
     12 
     13 3    需求:  想要将一个文件的数据打印在控制台上
     14 源:    文件
     15 目的:    控制台
     16 
     17 流操作的基本规律:
     18 最痛苦的就是流对象有很多,不知道该用哪一个。
     19 
     20 通过三个明确来完成。
     21 1    明确源和目的、
     22         源:    输入流。    InputStream        Reader
     23         目的:    输出流。    OutputStream    Writer
     24 2    操作的数据是否是纯文本。
     25         是:字符流
     26         不是: 字节流。
     27 3    当体系明确后,在明确要使用哪个具体的对象。
     28         通过设备来进行区分:
     29         源设备:  内存,硬盘,键盘
     30         目的设备: 内存,硬盘,控制台
     31 
     32 1    将一个文本文件中数据存储到另一个文件中,复制文件。
     33         源:因为是源,所以使用读取流。 InputStream Reader
     34         是不是操作文本文件、
     35         是! 这时  可以选择Reader
     36         接下来明确使用该体系中的哪个对象
     37         明确设备:硬盘。上的一个文件。
     38         Reader 体系中可以操作文件的对象是  FileReader
     39         是否需要高效:  是!  加入Reader体系中  缓冲区  BufferedReader 。
     40 
     41 
     42         FileReader fr=new FileReader("a.txt");
     43         BufferedReader bufr=new BufferedReader(fr);
     44 
     45 
     46         目的: OutputStream Writer
     47         是不是纯文本。
     48         是:  Writer
     49         设备:  硬盘,一个文件夹、
     50         Writer体系中可以操作文件的对象 FileWriter 。
     51         是否需要提高效率:  是!  加入Writer体系中缓冲区   BufferedWriter 。
     52 
     53         FileWriter fw=  new FileWriter("b.txt");
     54         BufferedWriter bufw=new BufferedWriter(fw);
     55 
     56 练习:  将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确
     57 
     58 --------------------------------------------------------------------
     59 2    需求:将键盘录入的数据保存到一个文件中。
     60             这个需求中有源和目的都存在
     61             那么分别分析:
     62             源:  InputStream  Reader
     63             是不是纯文本? 是  Reader
     64 
     65             设备:  键盘。对应的对象是 System.in
     66             不是选择Reader么? System.in对应的不是字节流么?
     67 
     68             为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。
     69             所以既然明确了Reader,那么久将System.in转换成Reader。
     70             用了Reader体系中转换流 InputStreamReader
     71             InputStreamReader isr=new InputStreamReader(System.in);
     72 
     73             需要提高效率么?  是  :   BufferedReader
     74             BufferedReader bufr=new BufferedReader(isr);
     75 
     76             目的: OutputStream  Writer
     77             是否是纯文本?  是 ;   Writer
     78             设备:硬盘。  中的一个文件。  使用 FileWriter
     79 
     80             FileWriter fw=new FileWriter("c.txt");
     81             需要提高效率么?   需要
     82             BufferedWriter bufw=new BufferedWriter(fw);
     83 
     84 
     85 
     86             *********************************
     87             扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
     88 
     89             目的: OutputStream  Writer
     90             是不是纯文本:  是   :  Writer
     91             设备:硬盘  中的一个文件  FileWriter
     92             但是FileWriter是使用的默认编码表。   GBK
     93 
     94             但是存储时,需要加入指定编码表utf-8.而指定的编码表只有转换流可以指定
     95             
     96             所以要使用的对象是OutputStreamWriter。、      
     97             而该转换流要接收一个字节输出流。而且还可以操作文本的字节输出流。 FileOutputStream
     98 
     99             OutputStreamWriter osw =new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
    100 
    101             需要高效么?  需要
    102             BufferedWriter bufw =new BufferedWriter(osw);
    103             所以,记住,转换流什么时候使用。字符和字节 之间的桥梁,通常,设计到字符编码转换时。
    104 
    105             需要用到转换流。
    106 
    107 
    108 练习:   将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。
    109 
    110 */
    111 
    112 class TranStreamDemo2
    113 {
    114     public static void main(String[] args)throws IOException
    115     {
    116         System.setIn(new FileInputStream("PersonDemo.java"));
    117         System.setOut(new PrintStream("zzz.txt"));
    118 
    119         //获取键盘录入对象。  
    120         //InputStream in=System.in;  
    121         //将字节流对象转成字符流对象,使用转换流。  
    122         //InputStreamReader isr=new InputStreamReader(in);  
    123         //为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader  
    124         //BufferedReader br=new BufferedReader(isr);  
    125   
    126         //键盘录入最常见写法  
    127         BufferedReader in=new BufferedReader(new InputStreamReader(System.in));  
    128   
    129         //字符流通向字节流  
    130         BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(System.out));  
    131   
    132         String s=null;  
    133         while((s=in.readLine())!=null)  
    134         {  
    135             if("over".equals(s))  
    136                 break;  
    137             bw.write(s.toUpperCase());//写入数据  
    138             bw.newLine();//换行  
    139             bw.flush();//刷新  
    140               
    141         }  
    142         bw.close();//关闭流资源  
    143         in.close();
    144         }
    145     }
    146 }

    小知识:

    1、异常的日志信息:

            当程序在执行的时候,出现的问题是不希望直接打印给用户看的,是需要作为文件存储起来,方便程序员查看,并及时调整的。

    示例:

     1 import java.io.*;  
     2 import java.text.*;  
     3 import java.util.*;  
     4 class  ExceptionInfo  
     5 {  
     6     public static void main(String[] args)   
     7     {  
     8         try  
     9         {  
    10             int[] arr =new int[2];  
    11             System.out.println(arr[3]);  
    12   
    13         }  
    14         catch (Exception e)  
    15         {  
    16             try  
    17             {  
    18                 Date d=new Date();//创建时间对象  
    19             //时间模块格式对象  
    20             SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");  
    21                 String s=sdf.format(d);  
    22   
    23                 PrintStream ps=new PrintStream("info.log");//打印流对象  
    24                 System.setOut(ps);//修改输出流设备  
    25                 ps.println(s);//输出时间  
    26                   
    27             }  
    28             catch (IOException ex)  
    29             {  
    30                 throw new RuntimeException("文件创建失败");  
    31             }  
    32             e.printStackTrace(System.out);//将异常信息输出指定输出流  
    33         }  
    34     }  
    35 }  

    2、系统属性信息存入文本

            获取系统信息:

                     Properties getProperties()

            将信息输出到指定输出流中

                     void list(PrintStream out)

            将输出流中数据存入指定文件中

                      new PrintStream("systeminfo.txt")

    示例:

    import java.util.*;    
    import java.io.*;    
      
    class SystemInfo     
    {    
       public static void main(String[] args)     
       {     
           PrintStream ps = null;    
           try    
           {    
              //获取系统信息:    
              Properties pop = System.getProperties();    
              //创建输出流对象,将输出流中数据存入指定文件中    
              ps = new PrintStream("systeminfo.txt");    
              //将属性列表输出到指定的输出流    
              pop.list(ps);    
           }    
           catch (Exception e)    
           {    
                throw new RuntimeException("获取系统信息失败。");    
           }    
        }    
    }  

    3、通过System类的setIn,setOut方法可以对默认设备进行改变

            System.setIn(newFileInputStream(“1.txt”));//将源改成文件1.txt。

            System.setOut(newFileOutputStream(“2.txt”));//将目的改成文件2.txt

    流的基本应用小结:

      1  流是用来处理数据的。

      2  处理数据时,一定要先明确数据源,与数据目的地(数据汇)。

      3  数据源可以是文件,可以是键盘。

      4  数据目的地可以是文件、显示器或者其他设备。

      5  流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理.转换处理等。、

    自我总结:

      流是用来操作数据的,并不能操作文件文件夹(例如修改只读模式等),而操作数据必定需要关联数据源,或者关联数据目的。数据源可以是内存,硬盘,键盘,目的源可以是控制台,内存,硬盘。而流的操作需要中转站,可以是自定义的缓冲区,也可以是数据流里的缓冲区,其中IO流的缓冲区定义了一些方便我们操作的方法。字节流的操作范围较字符流会更广一些,它可以操作音乐图片等字节流数据。而字符流操作的是我们看的懂的字符。通过IO流我们可以实现,复制,也可以把流切成n段,还可以将n段整合成完整的文件。因此总的来说IO流的作用很大,实现了信息的传递。

  • 相关阅读:
    并不对劲的loj3124:uoj477:p5405:[CTS2019]氪金手游
    并不对劲的loj6498. 「雅礼集训 2018 Day2」农民
    并不对劲的loj2251:p3688[ZJOI2017]树状数组
    并不对劲的loj2050:p3248:[HNOI2016]树
    并不对劲的BJOI2020
    并不对劲的loj3110:p5358:[SDOI2019]快速查询
    并不对劲的loj3111:p5359:[SDOI2019]染色
    (困难) CF 484E Sign on Fence,整体二分+线段树
    算法录 之 拓扑排序和欧拉路径。
    数据结构录 之 BST的高级应用。
  • 原文地址:https://www.cnblogs.com/ktlshy/p/4713934.html
走看看 - 开发者的网上家园