• 14:IO之字符字节流


    字节流:
    InputStream   
     OutputStream 
    字节流: 
    FileInputStream
    FileOutputStream
    BufferedInputStream
    BufferedOutputStream
    字符流:
    Writer Reader
    FileReader
    FileWriter
    BufferedReader
    BufferedWriter

    第一  IO流概述

    一、概述:

    IO流是来处理设备间的数据传输

    1、特点:

    1)流操作按照数据可分为字节流(处理所有的数据)和字符流(处理文字,其中包含编码表,可以指定编码表防止了编码表不同而产生乱码的现象)

    2)按照流向分可以分为输出流和输入流。

    字节流的抽象基类:InputStream(读)、OutputStream(写)

    字符流的抽象基类:Reader(读)、Writer(写)

    注:此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能;


    第二  字符流

    一、简述:

    1、字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。

    2、字符流只用于处理文字数据,而字节流可以任何数据。

    3、既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。专门用于操作文件的子类对象:FileWriter、FileReader

    二、写入字符流

    • 数据的续写是通过构造函数 FileWriter(String s,boolean append),根据给定文件名及指示是否附加写入数据的boolean值来构造FileWriter对象。为true时就是续写,为false就是不续写。 
    • 调用write()方法,将字符串写入到流中。这里他本身没有特定的写方法都是继承自父类的方法有写单个字符:write(int c),写入字符数组:write(char[] cbuf)这里的数组一般定义成1024的整数倍,不宜过大,过大容易造成内存溢出。写入字符数组的某一部分:write(char[] cbuf, int off, int len),写入字符串:write(String str),写入字符串的某一部分:write(String str, int off, int len)

    1.  *   
    2.  * 需求:在硬盘上,创建一个文件并写入一些文字数据。  
    3.  * 找到一个专门用于操作文件的Writer子类对象。FileWriter。  后缀名是父类名。 前缀名是该流对象的功能。  
    4.  */  
    5. public class FileWriterDemo {  
    6.     public static void main(String[] args) {  
    7.         method();//写内容  
    8.         method2();//续写内容  
    9.     }  
    10.       
    11.     /*  
    12.      * 写数据  
    13.      */  
    14.     public static void method(){  
    15.         FileWriter fw = null;  
    16.         try {  
    17.             //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。  
    18.             //该文件会被存放在指定的目录下,如果该文件已经存在,会被覆盖  
    19.             //其实该步就是在明确数据要存放的目的地。  
    20.             fw = new FileWriter("testwrite.txt");// C:\testwrite.txt  
    21.               
    22.             //调用write方法,将字符串写入到流中  
    23.             fw.write("asdsadafsd");  
    24.               
    25.             //刷新流对象中的缓冲中的数据。  
    26.             //将数据刷到目的地中。  
    27. //          fw.flush();  
    28.               
    29.             //关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。  
    30.             //将数据刷到目的地中。  
    31.             //和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。  
    32. //          fw.close();  
    33.         } catch (IOException e) {  
    34.             e.printStackTrace();  
    35.         }finally{  
    36.             try {  
    37.                 if(fw != null)  
    38.                     fw.close();  
    39.             } catch (IOException e) {  
    40.                 e.printStackTrace();  
    41.             }  
    42.         }  
    43.     }  
    44.       
    45.     /*  
    46.      * 在文件原有内容的基础上续写内容  
    47.      */  
    48.     public static void method2(){  
    49.         FileWriter fw = null;  
    50.         try {  
    51.             fw = new FileWriter("testwrite.txt", true);  
    52.             fw.write(" nihao xiexie");// 是换行符 是linux下的换行  
    53.         } catch (IOException e) {  
    54.             e.printStackTrace();  
    55.         }finally{  
    56.             if(fw != null){  
    57.                 try {  
    58.                     fw.close();  
    59.                 } catch (IOException e) {  
    60.                     e.printStackTrace();  
    61.                 }  
    62.             }  
    63.         }  
    64.     }  
    65. }  
       

    三、读取字符流

    • 创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。
    • 调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。(方法1)

      read()读取单个字符。read(char[] cbuf)将字符读入数组。(方法2)其实都是按照每次只读取一个字符的方式读取的,只是读到数组中会把读取到的数据存放在数组中,起到一个临时缓存的作用,提高了读取效率。

      1. 演示1
      2. public static void main(String[] args) throws IOException {
      3. FileReader fr = new FileReader("demo.txt");
      4. int ch = 0;
      5. while((ch=fr.read())!=-1){// abcde读5次
      6. System.out.println((char)ch);
      7. fr.close();
      8. }
      9. 演示2
      10. public class FileReaderDemo2 {
      11. public static void main(String[] args) throws IOException {
      12. FileReader fr = new FileReader("demo.txt");
      13. /*
      14. * 使用read(char[])读取文本文件数据。
      15. *
      16. * 先创建字符数组。
      17. */
      18. char[] buf = new char[1024];
      19. int len = 0;
      20. while((len=fr.read(buf))!=-1){ // abcde读1次,提高效率,是一个数组一个数组的读
      21. System.out.println(new String(buf,0,len));
      22. }
      23. /*
      24. int num = fr.read(buf);//将读取到的字符存储到数组中。读到几个几个变成数组
      25. System.out.println(num+":"+new String(buf,0,num));
      26. int num1 = fr.read(buf);//将读取到的字符存储到数组中。
      27. System.out.println(num1+":"+new String(buf,0,num1));
      28. int num2 = fr.read(buf);//将读取到的字符存储到数组中。
      29. System.out.println(num2+":"+new String(buf));
      30. */
      31.    fr.close(); 
      32. }



    四、文件的拷贝:

    原理:其实就是将磁盘下的文件数据读取出来,然后写入磁盘的一个文件中

    步骤:
    1、在硬盘上创建一个文件,用于存储读取出来的文件数据
    2、定义读取流和文件关联
    3、通过不断读写完成数据存储
    方式一:读取一个字符,存入一个字符
    方式二:先将读取的数据存入到内存中,再将存入的字符取出写入硬盘
    4、关闭流资源:输入流资源和输出流资源。


    1. 方法1
    2. /*
    3. * 思路:
    4. * 1,需要读取源,
    5. * 2,将读到的源数据写入到目的地。
    6. * 3,既然是操作文本数据,使用字符流。
    7. *
    8. */
    9. public class CopyTextTest {
    10. public static void main(String[] args) throws IOException {
    11. //1,读取一个已有的文本文件,使用字符读取流和文件相关联。
    12. FileReader fr = new FileReader("IO流_2.txt");
    13. //2,创建一个目的,用于存储读到数据。
    14. FileWriter fw = new FileWriter("copytext_1.txt");
    15. //3,频繁的读写操作。
    16. int ch = 0;
    17. while((ch=fr.read())!=-1){
    18. fw.write(ch);
    19. }
    20. //4,关闭流资源。先关写再关读
    21. fw.close();
    22. fr.close();
    23. }
    24. }
    1. 方法2
    2. public class CopyTextTest_2 {
    3. private static final int BUFFER_SIZE = 1024;
    4. public static void main(String[] args) {
    5. FileReader fr = null;
    6. FileWriter fw = null;
    7. try {
    8. fr = new FileReader("IO流_2.txt");
    9. fw = new FileWriter("copytest_2.txt");
    10. //创建一个临时容器,用于缓存读取到的字符。
    11. char[] buf = new char[BUFFER_SIZE];//这就是缓冲区。
    12. //定义一个变量记录读取到的字符数,(其实就是往数组里装的字符个数 )
    13. int len = 0;
    14. while((len=fr.read(buf))!=-1){
    15. fw.write(buf, 0, len);
    16. }
    17. } catch (Exception e) {
    18. // System.out.println("读写失败");
    19. throw new RuntimeException("读写失败");
    20. }finally{
    21. if(fw!=null)
    22. try {
    23. fw.close();
    24. } catch (IOException e) {
    25. e.printStackTrace();
    26. }
    27. if(fr!=null)
    28. try {
    29. fr.close();
    30. } catch (IOException e) {
    31. e.printStackTrace();
    32. }
    33. }
    34. }
    35. }

    第三  字符缓冲区

    一、写入缓冲区BufferedWriter
    1. /
    2. * 缓冲区的出现就是为了提高操作流的效率
    3. * 所以在创建缓冲区之前必须要先有流对象
    4. *
    5. * 缓冲区提供了一个跨平台的换行符 new Line();
    6. */
    7. public class BufferedWriterDemo {
    8. public static void main(String[] args) {
    9. method();
    10. }
    11. public static void method() {
    12. // 创建一个字符写入流对象
    13. FileWriter fw = null;
    14. BufferedWriter bfw = null;
    15. try {
    16. fw = new FileWriter("buf.txt");
    17. // 为了提高字符写入流效率。加入了缓冲技术。其实缓冲区就是封装了数组,不用自己定义数组,用起来更方便
    18. // 只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
    19. bfw = new BufferedWriter(fw);
    20. for (int x = 0; x < 10; x++) {
    21. bfw.write("abcds" + x);
    22. bfw.newLine();// 换行在windows中相当于 ,在linux中相当于
    23. }
    24. // 记住,只要用到缓冲区,就要记得刷新。
    25. // bufw.flush();
    26. } catch (IOException e) {
    27. e.printStackTrace();
    28. } finally {
    29. try {
    30. bfw.close();// 关闭缓冲区就是在关闭缓冲区中的流对象,关闭前刷新
    31. } catch (IOException e) {
    32. e.printStackTrace();
    33. }
    34. }
    35. }
    36. }
    1. private static final String LINE_SEPARATOR = System.getProperty("line.separator");
    2. //使用缓冲区的写入方法将数据先写入到缓冲区中。
    3. // bufw.write("abcdefq"+LINE_SEPARATOR+"hahahha");
    4. // bufw.write("xixiixii");
    5. // bufw.newLine(); 行分隔,就不需要LINE_SEPARATOR,不过只在这里有效
    6. // bufw.write("heheheheh");

    二、读取流缓冲区BufferedReader

    该缓冲区提供了一个一次读一行的方法readLine(),方便与对文本数据的获取,当返回null时,表示读到文件末尾。

          readLine()方法返回的时只返回回车符之前的数据内容,并不返回回车符,即读取的内容中不包含任何行终止符(回车符和换行符)。

    --->readLine()方法原理:无论是读一行,或读取多个字符,其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个

    1. public class BufferedReaderDemo {
    2. public static void main(String[] args) {
    3. method();
    4. }
    5. public static void method(){
    6. //创建一个读取流对象
    7. FileReader fr = null;
    8. BufferedReader bfr = null;
    9. try {
    10. fr = new FileReader("buf.txt");
    11. //为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
    12. bfr = new BufferedReader(fr);
    13. // char[] buf = new char[1024];
    14. // bfr.read(buf);
    15. // int len = 0;
    16. // while((len = bfr.read(buf)) != -1){
    17. // System.out.println(new String(buf,0,len));
    18. // }
    19. String line = null;
    20. while((line = bfr.readLine()) != null){
    21. System.out.println(line);
    22. }
    23. } catch (IOException e) {
    24. e.printStackTrace();
    25. }finally{
    26. try {
    27. bfr.close();
    28. } catch (IOException e) {
    29. e.printStackTrace();
    30. }
    31. }
    32. }
    33. }

    三、通过缓冲技术提高效率复制文件:
    1. public class CopyTextByBufTest {
    2. //不处理异常,抛出去
    3. public static void main(String[] args) throws IOException {
    4. FileReader fr = new FileReader("buf.txt");
    5. BufferedReader bufr = new BufferedReader(fr);
    6. FileWriter fw = new FileWriter("buf_copy.txt");
    7. BufferedWriter bufw = new BufferedWriter(fw);
    8. String line = null;
    9. while ((line = bufr.readLine()) != null) {
    10. bufw.write(line);
    11. bufw.newLine();
    12. bufw.flush();
    13. }
    14. /*
    15. * int ch = 0;
    16. *
    17. * while((ch=bufr.read())!=-1){
    18. *
    19. * bufw.write(ch); }
    20. */
    21. bufw.close();
    22. bufr.close();
    23. }
    24. }
    处理异常try...catch
    1. public class CopyByBuffer {
    2. public static void main(String[] args) {
    3. copyByBuf();
    4. }
    5. public static void copyByBuf() {
    6. FileWriter fw = null;
    7. FileReader fr = null;
    8. BufferedWriter bfw = null;
    9. BufferedReader bfr = null;
    10. try {
    11. fw = new FileWriter("C:\buffertest.txt");//创建写文件对象
    12. fr = new FileReader("buffertest.txt");//创建读文件对象
    13. bfw = new BufferedWriter(fw);//使用缓冲区关联读写对象
    14. bfr = new BufferedReader(fr);
    15. String line = null;
    16. while ((line = bfr.readLine()) != null) {//通过读一行的方式提高效率
    17. bfw.write(line);
    18. bfw.newLine();//换行,夸平台
    19. }
    20. } catch (IOException e) {
    21. e.printStackTrace();
    22. } finally {
    23. if (bfw != null) {
    24. try {
    25. bfw.close();
    26. } catch (IOException e) {
    27. e.printStackTrace();
    28. }
    29. }
    30. if (bfr != null) {
    31. try {
    32. bfr.close();
    33. } catch (IOException e) {
    34. e.printStackTrace();
    35. }
    36. }
    37. }
    38. }
    39. }


    四、自定义缓冲区
    其实就是模拟一个BufferedReader.
     * 分析:
     * 缓冲区中无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问。 其实这些方法最终操作的都是数组的角标。
     *
     * 缓冲的原理:
     * 其实就是从源中获取一批数据装进缓冲区中。在从缓冲区中不断的取出一个一个数据。
     * 在此次取完后,在从源中继续取一批数据进缓冲区。 当源中的数据取光时,用-1作为结束标记。
    1. public class MyBufferedReader extends Reader {
    2. private Reader r;
    3. //定义一个数组作为缓冲区。
    4. private char[] buf = new char[1024];
    5. //定义一个指针用于操作这个数组中的元素。当操作到最后一个元素后,指针应该归零。
    6. private int pos = 0;
    7. //定义一个计数器用于记录缓冲区中的数据个数。 当该数据减到0,就从源中继续获取数据到缓冲区中。
    8. private int count = 0;
    9. MyBufferedReader(Reader r){
    10. this.r = r;
    11. }
    12. /**
    13. * 该方法从缓冲区中一次取一个字符。
    14. * @return
    15. * @throws IOException
    16. */
    17. /*
    18. public int myRead() throws IOException{
    19. //从源中获取一批数据到缓冲区中。需要先做判断,只有计数器为0时,才需要从源中获取数据。
    20. if(count==0){
    21. count = r.read(buf); //这是在底层
    22. if(count<0)//没有了
    23. return -1;
    24. //每次获取数据到缓冲区后,角标归零(第一个read,底层)创建了一个数组。
    25. pos = 0;
    26. char ch = buf[pos];
    27. // 然后调用一次(第二个)read,返回一个a,再调用返回b,角标取一个,count减一个
    28. pos++;
    29. count--;
    30. return ch;
    31. }
    32. else if(count>0){//第二次调用的时候count就不为0了, 不需要count = r.read(buf)这一步了
    33. char ch = buf[pos];
    34. pos++;
    35. count--;
    36. return ch;
    37. }*/


    1. 优化后
    2. public int myRead() throws IOException{
    3. if(count==0){
    4. count = r.read(buf);
    5. pos = 0;
    6. }
    7. if(count<0)
    8. return -1;
    9. char ch = buf[pos++];
    10. count--;
    11. return ch;
    12. }
    五、模拟ReadLine
    1. public String myReadLine() throws IOException{
    2. // 定义一个临时容器,原BufferedReader封装的是字符数组,这里定义StringBuilder演示原理
    3. StringBuilder sb = new StringBuilder();
    4. int ch = 0;
    5. while((ch = myRead())!=-1){
    6. if(ch==' ') //如是' '继续往下读
    7. continue;
    8. if(ch==' ') //读到' '就停止读,然后将这些数据返回
    9. return sb.toString();
    10. //将从缓冲区中读到的字符,存储到缓存行数据的缓冲区中。
    11. sb.append((char)ch);
    12. }
    13. if(sb.length()!=0) //最后没有空格,说明有东西,返回,
    14. //这里是防止最后一行没有回车符的情况
    15. return sb.toString();
    16. return null;
    17. }
    18. public void myClose() throws IOException {
    19. r.close();
    20. }
    21. @Override
    22. public int read(char[] cbuf, int off, int len) throws IOException {
    23. return 0;
    24. }
    25. @Override
    26. public void close() throws IOException {
    27. }
    28. }
    29. 运行 ReadLine
    30. public class MyBufferedReaderDemo {
    31. public static void main(String[] args) throws IOException {
    32. FileReader fr = new FileReader("buf.txt");
    33. MyBufferedReader bufr = new MyBufferedReader(fr);
    34. String line = null;
    35. while((line=bufr.myReadLine())!=null){
    36. System.out.println(line);
    37. }
    38. bufr.myClose();
    39. Collections.reverseOrder();
    40. HashMap map = null;
    41. map.values();
    42. }
    43. }
    六、LineNumberReader(功能增强的读)

    在BufferedReader中有个直接的子类LineNumberReader,其中有特有的方法获取和设置行号:

    setLineNumber()和getLineNumber()

    1. public class LineNumberReaderDemo {
    2. public static void main(String[] args) throws IOException {
    3. FileReader fr = new FileReader("IO流_2.txt");
    4. LineNumberReader lnr = new LineNumberReader(fr);
    5. String line = null;
    6. lnr.setLineNumber(100);// 默认是0
    7. while ((line = lnr.readLine()) != null) {
    8. System.out.println(lnr.getLineNumber() + ":" + line);
    9. }
    10. lnr.close();
    11. }
    12. }


    第四  装饰设计模式

    装饰设计模式: (buffer就是装饰设计模式)
     对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。
      装饰和继承都能实现一样的特点:进行功能的扩展增强。 
    有什么区别呢? 
    首先有一个继承体系。
    Writer (抽取了一个写的)
     |--TextWriter (随便写的):用于操作文本
     |--MediaWriter:用于操作媒体。
     
    想要对操作的动作进行效率的提高。
    按照面向对象,可以通过继承对具体的进行功能的扩展。
    效率提高需要加入缓冲技术。
     
    Writer
     |--TextWriter:用于操作文本
      |--BufferTextWriter:加入了缓冲技术的操作文本的对象。
     |--MediaWriter:用于操作媒体。
      |--BufferMediaWriter:
    到这里就哦了。但是这样做好像并不理想。
    如果这个体系进行功能扩展,有多了流对象。
    那么这个流要提高效率,是不是也要产生子类呢?是。这时就会发现只为提高功能,进行的继承,
    导致继承体系越来越臃肿。不够灵活。
    重新思考这个问题?
    既然加入的都是同一种技术--缓冲。
    前一种是让缓冲和具体的对象相结合。
    可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。
    1. class Buffer {
    2. Buffer(TextWriter w) // 要缓冲谁就加入进来
    3. {
    4. }
    5. Buffer(MediaWirter w) // 要缓冲谁就加入进来
    6. {
    7. }
    8. } // 但是这样做,来新对象还得加入,比较麻烦
    9. // 都能写入,为了名字有意义,写成BufferWriter
    10. class BufferWriter extends Writer {
    11. BufferWriter(Writer w) {
    12. }
    13. }

    Writer
     |--TextWriter:用于操作文本
     |--MediaWriter:用于操作媒体。
     |--BufferWriter:用于提高效率。

      装饰比继承灵活。 
    特点:装饰类和被装饰类都必须所属同一个接口或者父类。 
    有个类想要增强,可以用装饰设计模式,把被装饰的类往里传进来就可以


    第五  字节流

    一、概述:

    1、字节流和字符流的原理是相似的,而字符流是基于字节流的,字节流可以操作如媒体等其他数据,如媒体(MP3,图片,视频)等

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

    3、InputStream     --->  输入流(读)  OutputStream  --->  输出流(写)

    4、为何不用进行刷流动作:因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。可直接将字节写入到指定文件中,但是需要在写代码的时候,如果有字符串,要将字符串转为字节数组再进行操作。

    5、FileInputStream特有方法:int available() --->  返回数据字节的长度,包含终止符

    在定义字节数组长度的时候,可以用到这个方法:byte[] = new byte[fos.available()]   (fos为字节流对象)但是,对于这个方法要慎用,如果字节过大,超过jvm所承受的大小(一般内存为64M),就会内存溢出。

    实例:
    1. /
    2. * 字节流操作
    3. * InputStream OutputStream
    4. *
    5. */
    6. public class FileStreamDemo {
    7. public static void main(String[] args) {
    8. // writeFile();
    9. // readFile_1();
    10. // readFile_2();
    11. readFile_3();
    12. }
    13. /*
    14. * 对字节流读操作的第一种方式 通过read()方法读取一个字节
    15. */
    16. public static void readFile_1() {
    17. FileInputStream fis = null;// 定义字节输入流
    18. try {
    19. fis = new FileInputStream("fos.txt");// 指定输入流和文件关联
    20. int ch = 0;
    21. while ((ch = fis.read()) != -1) {// 读取操作
    22. System.out.println((char) ch);
    23. }
    24. } catch (FileNotFoundException e) {
    25. e.printStackTrace();
    26. } catch (IOException e) {
    27. e.printStackTrace();
    28. } finally {
    29. try {
    30. if (fis != null)// 判空,提高程序的健壮性
    31. fis.close();
    32. } catch (IOException e) {
    33. e.printStackTrace();
    34. }
    35. }
    36. }
    37. /*
    38. * 对字节流读操作的第二种方式 通过read(byte[])方法读取字节数组
    39. */
    40. public static void readFile_2() {
    41. FileInputStream fis = null;
    42. try {
    43. fis = new FileInputStream("fos.txt");
    44. byte[] byf = new byte[1024];
    45. int len = 0;
    46. while ((len = fis.read(byf)) != -1) {
    47. System.out.println(new String(byf, 0, len));
    48. }
    49. } catch (FileNotFoundException e) {
    50. e.printStackTrace();
    51. } catch (IOException e) {
    52. e.printStackTrace();
    53. } finally {
    54. try {
    55. if (fis != null)
    56. fis.close();
    57. } catch (IOException e) {
    58. e.printStackTrace();
    59. }
    60. }
    61. }
    62. /*
    63. * 对字节流读操作的第三种方式 通过字节流的available()方法获取到文件大小,定义一个大小刚刚好的数组,无需循环 但是,这种方式操作较大数据时容易内存溢出,所以要慎用 首选的还是定义1024的整数倍数组
    64. */
    65. public static void readFile_3() {
    66. FileInputStream fis = null;
    67. try {
    68. fis = new FileInputStream("fos.txt");
    69. // 通过这样的方式定义一个刚刚好的数组
    70. // 这种方法慎用,如果文件不大,可以用这个方法
    71. byte[] byf = new byte[fis.available()];// fis.available()获取文件大小
    72. fis.read(byf);
    73. System.out.println(new String(byf));
    74. } catch (FileNotFoundException e) {
    75. e.printStackTrace();
    76. } catch (IOException e) {
    77. e.printStackTrace();
    78. } finally {
    79. try {
    80. fis.close();
    81. } catch (IOException e) {
    82. e.printStackTrace();
    83. }
    84. }
    85. }
    86. /*
    87. * 对字节流进行写操作
    88. */
    89. public static void writeFile() {
    90. FileOutputStream fos = null;
    91. try {
    92. fos = new FileOutputStream("fos.txt");//定义字节写出流和文件关联
    93. fos.write("abcds".getBytes());// str.getBytes()将String转化成字节数组
    94. } catch (FileNotFoundException e) {
    95. e.printStackTrace();
    96. } catch (IOException e) {
    97. e.printStackTrace();
    98. } finally {
    99. if (fos != null)
    100. try {
    101. fos.close();// 因为字节流没有用到缓冲区,所以不需要刷新,但必须关闭资源
    102. } catch (IOException e) {
    103. e.printStackTrace();
    104. }
    105. }
    106. }
    107. }
    二、通过字节流复制媒体文件

    思路:

    1、用字节流读取流对象和媒体文件相关联

    2、用字节写入流对象,创建一个媒体文件,用于存储获取到的媒体文件数据

    3、通过循环读写,完成数据的存储

    4、关闭资源

    1. * 通过字节流拷贝图片
    2. */
    3. public class CopyPicture {
    4. public static void main(String[] args) {
    5. copyPic();
    6. }
    7. public static void copyPic() {
    8. FileInputStream fis = null;
    9. FileOutputStream fos = null;
    10. try {
    11. fis = new FileInputStream("1.jpg");
    12. fos = new FileOutputStream("C:\1.jpg");
    13. byte[] byf = new byte[1024];
    14. int len = 0;
    15. while ((len = fis.read(byf)) != -1) {// 将数据读取到数组中
    16. fos.write(byf, 0, len);// 写入数组中的有效数据
    17. }
    18. } catch (FileNotFoundException e) {
    19. e.printStackTrace();
    20. } catch (IOException e) {
    21. e.printStackTrace();
    22. } finally {
    23. // 有多个流不能一起关闭,要分别关闭
    24. if (fis != null) {
    25. try {
    26. fis.close();
    27. } catch (IOException e) {
    28. e.printStackTrace();
    29. }
    30. }
    31. if (fos != null) {
    32. try {
    33. fos.close();
    34. } catch (IOException e) {
    35. e.printStackTrace();
    36. }
    37. }
    38. }
    39. }
    40. }
    41. // 千万不要用,效率没有!
    42. public static void copy_4() throws IOException {
    43. FileInputStream fis = new FileInputStream("c:\0.mp3");
    44. FileOutputStream fos = new FileOutputStream("c:\4.mp3");
    45. int ch = 0;
    46. while((ch =fis.read())!=-1){
    47. fos.write(ch);
    48. }
    49. fos.close();
    50. fis.close();
    51. }
    52. //不建议。
    53. public static void copy_3() throws IOException {
    54. FileInputStream fis = new FileInputStream("c:\0.mp3");
    55. FileOutputStream fos = new FileOutputStream("c:\3.mp3");
    56. byte[] buf = new byte[fis.available()];
    57. fis.read(buf);
    58. fos.write(buf);
    59. fos.close();
    60. fis.close();
    61. }
    62. //提高效率
    63. public static void copy_2() throws IOException {
    64. FileInputStream fis = new FileInputStream("c:\0.mp3");
    65. BufferedInputStream bufis = new BufferedInputStream(fis);
    66. FileOutputStream fos = new FileOutputStream("c:\2.mp3");
    67. BufferedOutputStream bufos = new BufferedOutputStream(fos);
    68. int ch = 0;
    69. while((ch=bufis.read())!=-1){
    70. bufos.write(ch);
    71. }
    72. bufos.close();
    73. bufis.close();
    74. }
    三、字节流缓冲区

    1、缓冲区的出现无非就是提高了流的读写效率,当然也就是因为这样,所以缓冲区的使用频率也是相当高的,所以要做必要性的掌握。

    2、字节流缓冲区读写的特点:

    这里没有什么特殊的读写方法,就是read()读一个字节,read(byte[])读数组的方法。写write(int b)写一个自己,write(byte[])写数组的方法。

    注:需要注意个地方,write方法只写出二进制的最后八位。

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

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

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

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

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

    5)到了文件的结尾处,存入最后一组数据,当取完数组中的元素,就会减少到0,这是全部数据就取完了

    3、自定义字节缓冲区:
    1. * 自定义字节流缓冲区
    2. */
    3. public class MyBufferedInputStream {
    4. private InputStream in;
    5. private byte[] buf = new byte[1024];
    6. private int pos;// 定义数组的指针
    7. private int count;// 定义计数器
    8. MyBufferedInputStream(InputStream in) {
    9. this.in = in;
    10. }
    11. // 一次读一个字节,从缓冲区(数组)中取得
    12. public int myRead() throws IOException {
    13. // 通过in对象读取数据并存放在buf数组中
    14. if (count == 0) {
    15. count = in.read(buf);
    16. if (count < 0) {
    17. return -1;
    18. }
    19. pos = 0;
    20. byte b = buf[pos];
    21. count--;
    22. pos++;
    23. return b & 255;// &255是因为存放的是二进制,也就是可能前八位是11111111,提升为int还是-1,就直接return了,
    24. // &255就是让前面补0
    25. /*
    26. * 11111111 11111111 11111111 11111111
    27. * &00000000 00000000 00000000 11111111
    28. * ------------------------------------
    29. * 00000000 00000000 00000000 11111111
    30. */
    31. }else if(count>0){
    32. byte b = buf[pos];
    33. count--;
    34. pos++;
    35. return b&255;//&0xff
    36. }
    37. return -1;
    38. }
    39. public void myClose() throws IOException{
    40. in.close();
    41. }
    42. }
    注:取出的是byte型,返回的是int型,这里存在提升的动作,
    当byte中的八位全为1的时候是byte的-1,提升为int类型,就变为int型的-1,为-1时程序就停止循环了,read循环条件就结束了,变为-1的原因是由于在提升时,将byte的八位前都补的是1,即32位的数都是1,即为int型的-1了。如何保证提升后的最后八位仍为1呢?就需要将前24位补0,就可以保留原字节数据不变,又可以避免转为int型出现-1的情况;
    那么要如何做呢?
    这就需要将提升为int的数据和前24位为0,后八位仍为原字节数据的这个值做与运算。即和255做与运算即可。说到了这里应该也明白了为什么Read方法返回值为int类型了。


    第六  转换流

    转换流:转换流可以实现字节数据和字符数据的相互转换方便与操作,而且在转换的时候可以指定编码,这也是该流最具特色的地方。:

    InputStreamReader 是字节流通向字符流的桥梁

    OutputStreamWriter 是字符流通向字节流的桥梁


    转换流的子类和转换流的区别?
    InputStreamReader   字节-->字符 
     |--FileReader : 字节流+本地默认码表。
    OutputStreamWriter  字符-->字节
     |--FileWriter 

    什么时候使用转换流呢?
     1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。
      提高对文本操作的便捷。
     2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。
     BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));

    1.       //获取键盘录入对象  
    2.          InputStream in = System.in;  
    3.           
    4.          //将字节流对象转换成字符流对象  
    5.          InputStreamReader isr = new InputStreamReader(in);  
    6.           
    7.          //将字符流对象用缓冲技术高效处理  
    8.          BufferedReader bufr = new BufferedReader(isr);
    9.          // 键盘录入最常见的写法  
    10.          BufferedReader bufr = new BufferedReader(new  InputStreamReader(System.in));  


    字符流转字节流:

      1.         // 操作输出  
      2.         // 获取输出流  
      3.          OutputStream out = System.out;  
      4.          //将字符流转换成字节流,OutputStreamWriter字符流通向字节流的桥梁  
      5.          OutputStreamWriter osw = new OutputStreamWriter(out);  
      6.          BufferedWriter bfw = new BufferedWriter(osw);//高效缓冲区

    获取键盘输入:键盘输入都是字节,所以用到字节流与标准输入流相关联就可以把输入的数据获取到流中,以达到数据的操作效果。
    1. * 获取键盘录入
    2. public class ReadIn {
    3. public static void main(String[] args) {
    4. // method_1();
    5. // method_2();
    6. InputStreamReaderDemo();
    7. }
    8. /*
    9. * 获取键盘录入
    10. */
    11. public static void method_1() {
    12. InputStream in = System.in;// 定义输入流与键盘输入流相关联
    13. int ch = 0;
    14. try {
    15. while ((ch = in.read()) != -1) {
    16. System.out.println(ch);
    17. }
    18. } catch (IOException e) {
    19. e.printStackTrace();
    20. }
    21. }
    22. /*
    23. * 需求:通过键盘录入数据。 当录入一行数据后,就将该行数据进行打印。 如果录入的数据是over,那么停止录入。
    24. * 1,因为键盘录入只读取一个字节,要判断是否是over,需要将读取到的字节拼成字符串。
    25. * 2,那就需要一个容器。StringBuilder.
    26. * 3,在用户回车之前将录入的数据变成字符串判断即可。
    27. */
    28. public static void method_2() {
    29. InputStream in = System.in;
    30. StringBuilder sb = new StringBuilder();// 定义一个临时容器
    31. while (true) {
    32. int ch = 0;
    33. try {
    34. ch = in.read();
    35. if (ch == ' ')// windows中换行符为
    36. continue;
    37. if (ch == ' ') {
    38. String s = sb.toString();// 当读到一行的结束标记时,把改行数据变成字符串
    39. if ("over".equals(s))
    40. break;
    41. System.out.println(s.toUpperCase()); //变成大写
    42. sb.delete(0, sb.length());// 清空容器 ,不清空会将上次输入的和这次输入的都输出
    43. } else {
    44. sb.append((char) ch); //将读取到的字节存储到StringBuilder中。
    45. }
    46. } catch (IOException e) {
    47. e.printStackTrace();
    48. }
    49. }
    50. }
    51. // **************************************************************************
    52. /*
    53. * 通过上面录入一行数据,发现其方法类似于readLine方法 但是readLine方法是字符流缓冲区的方法,所以需要把字节流转换成字符流进行操作
    54. * 需要用到InputStreamReader方法进行转换,InputStreamReader字节通向字符的桥梁
    55. *
    56. * 转换流
    57. */
    58. public static void InputStreamReaderDemo() {
    59. BufferedReader bufr = null;
    60. try {
    61. // 源为文件
    62. bufr = new BufferedReader(new InputStreamReader(
    63. new FileInputStream("buf.txt")));
    64. } catch (FileNotFoundException e2) {
    65. e2.printStackTrace();
    66. }
    67. // 操作输出
    68. // 目的地是控制台
    69. BufferedWriter bfw = new BufferedWriter(new OutputStreamWriter(
    70. System.out));
    71. // 目的地是文件
    72. // BufferedWriter bfw = null;
    73. // try {
    74. // bfw = new BufferedWriter(new OutputStreamWriter(new
    75. // FileOutputStream("out.txt")));
    76. // } catch (FileNotFoundException e1) {
    77. // e1.printStackTrace();
    78. // }
    79. String line = null;
    80. try {
    81. while ((line = bufr.readLine()) != null) {
    82. if ("over".equals(line)) {
    83. break;
    84. }
    85. bfw.write(line.toUpperCase());
    86. bfw.newLine();// 换行
    87. bfw.flush();// 数据存放在缓冲区,所以需要刷新
    88. }
    89. } catch (IOException e) {
    90. e.printStackTrace();
    91. } finally {
    92. try {
    93. bufr.close();
    94. } catch (IOException e) {
    95. e.printStackTrace();
    96. }
    97. }
    98. }
    99. }
    100. 不处理异常
    101. public class TransStreamDemo2 {
    102. public static void main(String[] args) throws IOException {
    103. /*
    104. * 1,需求:将键盘录入的数据写入到一个文件中。
    105. *
    106. * 2,需求:将一个文本文件内容显示在控制台上。
    107. *
    108. * 3,需求:将一个文件文件中的内容复制到的另一个文件中。
    109. */
    110. BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
    111. BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt")));
    112. String line = null;
    113. while((line=bufr.readLine())!=null){
    114. if("over".equals(line))
    115. break;
    116. bufw.write(line.toUpperCase()); //变成大写
    117. bufw.newLine();
    118. bufw.flush();
    119. }
    120. }
    121. }


    第七  流的操作规律

    之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。
    想要知道开发时用到哪些对象。只要通过四个明确即可。
    1,明确源和目的(汇)
     源:InputStream  Reader
     目的:OutputStream  Writer
    2,明确数据是否是纯文本数据。
     源:是纯文本:Reader
      否:InputStream
     目的:是纯文本 Writer
      否:OutputStream
     
     到这里,就可以明确需求中具体要使用哪个体系。
     
    3,明确具体的设备。
     源设备:
      硬盘:File
      键盘:System.in
      内存:数组
      网络:Socket流
     
     目的设备:
      硬盘:File
      控制台:System.out
      内存:数组
      网络:Socket流
    4,是否需要其他额外功能。
     1,是否需要高效(缓冲区);
      是,就加上buffer.
     2,转换。
     
    需求1:复制一个文本文件。
     1,明确源和目的。
      源:InputStream Reader
      目的:OutputStream  Writer
     2,是否是纯文本?
      是!
      源:Reader
      目的:Writer
     
     3,明确具体设备。
      源:
       硬盘:File
      目的:
       硬盘:File
     
      FileReader fr = new FileReader("a.txt");
      FileWriter fw = new FileWriter("b.txt");
     
     4,需要额外功能吗?
      需要,需要高效。
      BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
      BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
     
    ================================================
    需求2:读取键盘录入信息,并写入到一个文件中。
     
     1,明确源和目的。
      源:InputStream Reader
      目的:OutputStream  Writer
     2,是否是纯文本呢?
      是,
      源:Reader
      目的:Writer
     3,明确设备
      源:
       键盘。System.in
      目的:
       硬盘。File
       
      InputStream in = System.in;
      FileWriter fw = new FileWriter("b.txt");
      这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
     4,需要额外功能吗?
      需要。转换。 将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
       所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
      InputStreamReader isr = new InputStreamReader(System.in);
      FileWriter fw = new FileWriter("b.txt");
     
      还需要功能吗?
      需要:想高效。
      BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
      BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
     
       
     
    ===================================================
     
    需求3:将一个文本文件数据显示在控制台上。
     1,明确源和目的。
      源:InputStream Reader
      目的:OutputStream  Writer
     2,是否是纯文本呢?
      是,
      源:Reader
      目的:Writer
     3,明确具体设备
      源:
       硬盘:File
      目的:
       控制台:System.out
       
      FileReader fr = new FileReader("a.txt");
      OutputStream out = System.out;//PrintStream
     4,需要额外功能吗?
      需要,转换。
      FileReader fr= new FileReader("a.txt");
      OutputStreamWriter osw = new OutputStreamWriter(System.out);
      需要,高效。
      BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
      BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
     
    ================================================================
    需求4:读取键盘录入数据,显示在控制台上。
     1,明确源和目的。
      源:InputStream Reader
      目的:OutputStream  Writer
     2,是否是纯文本呢?
      是,
      源:Reader
      目的:Writer
     3,明确设备。
      源:
       键盘:System.in
      目的:
       控制台:System.out
     
      InputStream in = System.in;
      OutputStream out = System.out;
     
     4,明确额外功能?
      需要转换,因为都是字节流,但是操作的却是文本数据。
      所以使用字符流操作起来更为便捷。
      InputStreamReader isr = new InputStreamReader(System.in);
      OutputStreamWriter osw = new OutputStreamWriter(System.out);
     
      为了将其高效。
      BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
      BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
     
     
    ============================================================
    5,将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
     
     1,目的。OutputStream,Writer
     2,是纯文本,Writer。
     3,设备:硬盘File
     FileWriter fw = new FileWriter("a.txt");
     fw.write("你好");
     
     注意:既然需求中已经明确了指定编码表的动作。
     那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。
     只能使用其父类。OutputStreamWriter.
     OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream
     
     OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName(编码));
     
     需要高效吗?
     BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));

      演示:将一个中文字符串数据按照指定的编码表写入到一个文本文件中. 
    写:
    1. public static void writeText_2() throws IOException {
    2. OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
    3. "gbk_3.txt"), "GBK");
    4. // OutputStreamWriter osw = new OutputStreamWriter(new
    5. // FileOutputStream("gbk_3.txt"),"GBK");
    6. // FileWriter fw = new FileWriter("gbk_1.txt");
    7. /*
    8. * 这两句代码的功能是等同的。 FileWriter:其实就是转换流指定了本机默认码表的体现。而且这个转换流的子类对象,可以方便操作文本文件。
    9. * 简单说:操作文件的字节流+本机默认的编码表。 这是按照默认码表来操作文件的便捷类。
    10. *
    11. * 如果操作文本文件需要明确具体的编码。FileWriter就不行了。必须用转换流
    12. */
    13. osw.write("你好");
    14. osw.close();
    15. }
    16. public static void writeText_3() throws IOException {
    17. OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
    18. "u8_1.txt"), "UTF-8");
    19. osw.write("你好");
    20. osw.close();
    21. }

    读:
    1. public static void readText_1() throws IOException {
    2. FileReader fr = new FileReader("gbk_1.txt");
    3. char[] buf = new char[10];
    4. int len = fr.read(buf);
    5. String str = new String(buf,0,len);
    6. System.out.println(str);
    7. fr.close();
    8. }
    9. //用utf-8读默认的编码文件
    10. public static void readText_2() throws IOException, FileNotFoundException {
    11. InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk_1.txt"),"utf-8");
    12. char[] buf = new char[10];
    13. int len = isr.read(buf);
    14. String str = new String(buf,0,len);
    15. System.out.println(str);
    16. isr.close();
    17. }
    18. }
    19. }


     




  • 相关阅读:
    数学--数论--HDU 2136(素数筛选法)
    思维+模拟--POJ 1013 Counterfeit Dollar
    数学--数论--莫比乌斯函数
    事半功倍和事倍功半
    一道阿里面试题
    文本分类学习(三) 特征权重(TF/IDF)和特征提取
    汉字字典树
    文本分类学习(二)文本表示
    文本分类学习(一):开篇
    一道网易面试编程题
  • 原文地址:https://www.cnblogs.com/sixrain/p/4912372.html
Copyright © 2020-2023  润新知