zoukankan      html  css  js  c++  java
  • Java IO(二)

       字节流

       字符流:

    1. FileReader
    2. FileWriter
    3. BufferedReader
    4. BufferedWriter

       字节流:

    1. FileInputStream 
    2. FileOutputStream
    3. BufferedInputStream 
    4. BufferedOutputStream

       想要操作图片数据,这时就要用到字节流。

       示例代码如下:

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class FileOutputStreamDemo0 {
    
        public static void main(String[] args) throws IOException {
            //writeFile();
            //readFile();
            
            //readFile_2();
            
            readFile_3();
        }
        
        public static void readFile_3() throws IOException {
            FileInputStream fis = new FileInputStream("fos.txt");
            
            //int num = fis.available();
            
            byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区,不用在循环了。不过慎用!!!
            
            fis.read(buf);
            
            //System.out.println("num = " + num);
            System.out.println(new String(buf));
            
            fis.close();
        }
        
        public static void readFile_2() throws IOException {
            FileInputStream fis = new FileInputStream("fos.txt");
            
            byte[] buf = new byte[1024];//还是建议使用1024的整数倍开辟字节数组
            
            int len = 0;
            
            while((len = fis.read(buf)) != -1) {
                System.out.println(new String(buf, 0, len));
            }
            
            fis.close();
        }
        
        public static void readFile() throws IOException {
            FileInputStream fis = new FileInputStream("fos.txt");
            
            int ch = 0;
            
            while((ch = fis.read()) != -1) {
                System.out.println((char) ch);
            }
            
            fis.close();
        }
        
        public static void writeFile() throws IOException {
            FileOutputStream fos = new FileOutputStream("fos.txt");
            
            fos.write("abcde".getBytes());
            
            fos.close();
        }
    
    }

       练习1:复制一个图片。

       思路:

    1. 用字节读取流对象和图片关联。
    2. 用字节写入流对象创建一个图片文件,用于存储获取到的图片数据。
    3. 通过循环读写,完成数据的存储。
    4. 关闭资源。

       代码:

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class CopyPic {
    
        public static void main(String[] args) {
            FileOutputStream fos = null;
            FileInputStream fis = null;
            
            try {
                fos = new FileOutputStream("E:\MyJava\workspace\IO_Project\Krystal.jpg");
                fis = new FileInputStream("D:\Krystal.jpg");
                
                byte[] buf = new byte[1024];
                
                int len = 0;
                
                while((len = fis.read(buf)) != -1) {
                    fos.write(buf, 0, len);
                }
            } catch (IOException e) {
                throw new RuntimeException("复制失败!");
            } finally {
                try {
                    if(fis != null) 
                        fis.close();
                } catch (IOException e) {
                    throw new RuntimeException("读取关闭失败!");
                }
                try {
                    if(fos != null) 
                        fos.close();
                } catch (IOException e) {
                    throw new RuntimeException("写入关闭失败!");
                }
            }
        }
    
    }

       练习2:复制一个MP3文件。

       通过缓冲区(BufferedOutputStream/BufferedInputStream),演示MP3的复制。

       代码:

    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class CopyMp3 {
    
        public static void main(String[] args) throws IOException {
            long start = System.currentTimeMillis();
            copy_2();
            long end = System.currentTimeMillis();
            System.out.println((end - start) + "毫秒");
        }
        /*
         * 通过字节流的缓冲区完成复制
         */
        public static void copy_1() throws IOException {
            BufferedInputStream bufis = new BufferedInputStream(
                    new FileInputStream("E:\KuGou\Temp\滨琦步 - 犬夜叉主题曲.mp3"));
            
            BufferedOutputStream bufos = new BufferedOutputStream(
                    new FileOutputStream("E:\MyJava\workspace\IO_Project\滨琦步 - 犬夜叉主题曲.mp3"));
            
            int by = 0;
            
            while((by = bufis.read()) != -1) {
                bufos.write(by);
            }
            
            bufis.close();
            bufos.close();
        }
        
    }

       练习3:自定义一个类模拟BufferedInputStream,完成一个MP3文件的复制。

       分析:

    mp3是由二进制数据组成的:
    11111111-1110000000000000000000101010110111010111010010110001
    
    问题:自定义的myRead()函数为什么会返回int类型,而不直接返回byte类型呢?
    分析:
    byte: -1  ----> int: -1
    
    11111111(-1)提升为11111111 11111111 11111111 11111111(-1)
    11111111(-1)--->提升为一个int类型,那还不是-1吗?是-1的原因是因为在8个1前面补的都是1导致的。
    那么我只要在前面补0,即可以保留原字节数据不变,又可以避免-1的出现。
    怎么补0呢?
        11111111 11111111 11111111 11111111
    &   00000000 00000000 00000000 11111111
    ------------------------------------------
        00000000 00000000 00000000 11111111
        
    所以应把11111111(-1)提升为00000000 00000000 00000000 11111111(255),避免返回-1这种情况

       代码:

    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    class MyBufferedInputStream {
        private InputStream in;
        private byte[] buf = new byte[1024 * 4];
        private int pos = 0, count = 0;
        
        MyBufferedInputStream(InputStream in) {
            this.in = in;
        }
          
        /*
         * 一次读一个字节,从缓冲区(字节数组)获取。
         */
        public int myRead() throws IOException {
            /*
             * 通过in对象读取硬盘上数据,并存储到buf中。
             */
            if(count == 0) {
                count = in.read(buf);
                if(count < 0)
                    return -1;
                pos = 0;
                byte b = buf[pos];
                count--;
                pos++;
                return b & 255;
            } else if(count > 0) {
                byte b = buf[pos];
                count--;
                pos++;
                return b & 0xff;
            }
            return -1;
        }
        
        public void myClose() throws IOException {
            in.close();
        }
    }
    public class MyBufferedInputStreamDemo {
    
        public static void main(String[] args) throws IOException {
            long start = System.currentTimeMillis();
            copy_2();
            long end = System.currentTimeMillis();
            System.out.println((end - start) + "毫秒");
        }
        
        public static void copy_2() throws IOException {
            MyBufferedInputStream bufis = new MyBufferedInputStream(
                    new FileInputStream("E:\KuGou\Temp\滨琦步 - 犬夜叉主题曲.mp3"));
            
            BufferedOutputStream bufos = new BufferedOutputStream(
                    new FileOutputStream("E:\MyJava\workspace\IO_Project\滨琦步 - 犬夜叉主题曲_2.mp3"));
            
            int by = 0;
            
            //System.out.println("第一个字节:" + bufis.myRead());//第一个字节:-1,因为读到了连续的11111111
            
            while((by = bufis.myRead()) != -1) {
                bufos.write(by);
            }
            
            bufis.myClose();
            bufos.close();
        }
    
    }

       System类对IO的支持

       读取键盘录入。

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

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

       例,需求:通过键盘录入数据。当录入一行数据后,就将该行数据进行打印。如果录入的数据是over,那么录入停止。

       代码:

    import java.io.IOException;
    import java.io.InputStream;
    
    public class ReadIn {
    
        public static void main(String[] args) throws IOException {
            InputStream in = System.in;
            StringBuilder sb = new StringBuilder();
            while(true) {
                int ch = in.read();
                if(ch == '
    ')
                    continue;
                if(ch == '
    ') {
                    String s = sb.toString();
                    if("over".equals(s))
                        break;
                    System.out.println(s.toUpperCase());
                    sb.delete(0, sb.length());//清空缓冲区
                } else {
                    sb.append((char) ch);
                }
                
            }
            
            //System.out.println('
    ' + 0);//13
            //System.out.println('
    ' + 0);//10
            /*
            int by = in.read();
            int by1 = in.read();
            int by2 = in.read();
            
            System.out.println(by);
            System.out.println(by1);
            System.out.println(by2);
            */
        }
    }

       通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理。也就是readLine()方法。

       能不能直接使用readLine()方法来完成键盘录入的一行数据的读取呢?

       readLine()方法是字符流BufferedReader类中的方法,而键盘录入的read()方法是字节流InputStream的方法,那么能不能将字节流转成字符流,再使用字符流缓冲区的readLine()方法呢?

       此时就需要用到转换流。

       转换流:

    1. InputStreamReader字节流通向字符流的桥梁:它使用指定的charset读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集(GBK)
    2. OutputStreamWriter字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。 

       上例优化之后的代码为:

    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    
    public class TransStreamDemo0 {
    
        public static void main(String[] args) throws IOException {
            /*
             * 获取键盘录入对象。
             */
            //InputStream in = System.in;
            
            /*
             * 将字节流对象转成字符流对象,使用转换流——InputStreamReader
             */
            //InputStreamReader isr = new InputStreamReader(in);
            
            /*
             * 为了提高效率,将字符流进行缓冲区技术高效操作,使用BufferedReader
             */
            //BufferedReader bufr = new BufferedReader(isr);
            /*
             * 简写格式,键盘录入最常见写法
             */
            BufferedReader bufr = 
                    new BufferedReader(new InputStreamReader(System.in));
            
            //OutputStream out = System.out;//屏幕输出
            /*
             * 字符流输出对象转换成字节流输出对象
             */
            //OutputStreamWriter osw = new OutputStreamWriter(out);
            
            //BufferedWriter bufw = new BufferedWriter(osw);
            /*
             * 简写格式
             */
            BufferedWriter bufw = 
                    new BufferedWriter(new OutputStreamWriter(System.out));
            
            String str = null;
            while((str = bufr.readLine()) != null) {
                if("over".equals(str))
                    break;
                //System.out.println(str.toUpperCase());
                bufw.write(str.toUpperCase());
                bufw.newLine();
                bufw.flush();
            }
            bufw.close();//并没有写bufr.close();是因为此程序一结束掉,就给关闭了。
        }
    
    }

       

       流操作的基本规律

       最痛苦的就是流对象有很多,不知道该用哪一个。

       通过三个明确来完成:

      1、明确源和目的。

        源:输入流。InputStream Reader

        目的:输出流。OutputStream Writer

      2、操作的数据是否是纯文本。

        是:字符流。

        不是:字节流。

      3、当体系明确后,在明确要使用哪个具体的对象。

        通过设备来进行区分:

        源设备:内存、硬盘、键盘。

        目的设备:内存、硬盘、控制台。

       例1、需求:将一个文本文件中的数据存储到另一个文件中。(复制文件)

      分析:

        源:因为是源,所以使用读取流。InputStream Reader

        是不是操作文本文件?

        是!这时就可以选择Reader,这样体系就明确了。

        接下来要明确要使用该体系中的那个对象?

        明确设备:硬盘上的一个文件。Reader体系中可以操作文件的对象是FileReader。

        是否需要提高效率?是!加入Reader体系中的缓冲区BufferedReader。

    FileReader fr = new FileReader("a.txt");
    BufferedReader bufr = new BufferedReader(fr);
    

        目的:OutputStream Writer

        目的是否是纯文本?是!Writer。

        设备:硬盘上的一个文件。

        Writer体系中可以操作文件的对象是FileWriter。

        是否需要提高效率?是!加入Reader体系中的缓冲区BufferedWriter。

    FileWriter fw = new FileWriter("b.txt");
    BufferedWriter bufw = new BufferedWriter(fr);
    

       练习:将一个图片文件中的数据存储到另一个文件中。(复制文件)

       代码:

    /*
     * 练习:将一个图片文件中的数据存储到另一个文件中。(复制文件)
     */
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class test0 {
    
        public static void main(String[] args) {
            FileInputStream fis = null;
            FileOutputStream fos = null;
            BufferedInputStream bis = null;
            BufferedOutputStream bos = null;
            byte[] by = new byte[1024];
            int len = 0;
            try {
                fis = new FileInputStream("d:\Krystal.jpg");
                bis = new BufferedInputStream(fis);
                
                fos = new FileOutputStream("d:\java\io123\Krystal.jpg");
                bos = new BufferedOutputStream(fos);
                
                while((len = bis.read(by)) != -1) {
                    bos.write(by, 0, len);
                }
            }  catch (IOException e) {
                throw new RuntimeException("图片复制失败!");
            } finally {
                try {
                    if(bis != null) 
                        bis.close();
                } catch (IOException e) {
                    throw new RuntimeException("关闭读取流失败!");
                }
                try {
                    if(bos != null) 
                        bos.close();
                } catch (IOException e) {
                    throw new RuntimeException("关闭写入流失败!");
                }
            }
        }
    
    }

       例2、需求:将键盘录入的数据保存到一个文件中。

      分析:

        这个需求中有源和目的都存在。那么分别分析

        源:InputStream Reader

        是不是纯文本?是!Reader

        设备:键盘。对应的对象是System.in。

        不是选择Reader吗?System.in对应的不是字节流吗?为了操作键盘的文本数据方便,转成字符流,按照字符串操作是最方便的。所以既然明确了Reader,那么就将System.in转换成字符流Reader。用到了Reader体系中的转换流,InputStreamReader

    InputStreamReader isr = new InputStreamReader(System.in);
    

        需要提高效率吗?需要!BufferedReader 

    BufferedReader bufr = new BufferedReader(isr);
    

        目的:OutputStream Writer

        是不是纯文本?是!Writer

        设备:硬盘上的一个文件。使用FileWriter。

    FileWriter fw = new FileWriter("c.txt");
    

        需要提高效率吗?需要!BufferedWriter

    BufferedWriter bufw = new BufferedWriter(fw);
    

      扩展一下,想要把录入的数据按照指定的编码表(UTF-8),将数据存到文件中,怎么办呢?

      分析:

        目的:OutputStream Writer

        是不是纯文本?是!Writer

        设备:硬盘上的一个文件。使用FileWriter。但是FileWriter是使用的默认编码表(GBK)

        但是存储时,需要加入指定的编码表(UTF-8),而指定的编码表只有转换流可以指定,所以要使用的对象是OutputStreamWriter。而该转换流对象要接收一个字节输出流,而且还可以操作文件的字节输出流,FileOutputStream。

    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"), "UTF-8");
    

        需要提高效率吗?需要!BufferedWriter

    BufferedWriter bufw = new BufferedWriter(osw);
    

      所以,记住,转换流什么时候使用?字符和字节之间的桥梁,通常涉及到字符编码转换时,需要用到转换流

       练习:将一个文本数据打印在控制台上。

       代码:

    /*
     * 练习:将一个文本数据打印在控制台上。
     */
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    
    public class test1 {
    
        public static void main(String[] args) {
            BufferedReader br = null;
            BufferedWriter bw = null;
            String line = null;
                    
            try {
                br = new BufferedReader(new FileReader("d:\java\ArrayTool.java"));
                bw = new BufferedWriter(new OutputStreamWriter(System.out));
    
                while((line = br.readLine()) != null) {
                    bw.write(line);
                    bw.newLine();
                    bw.flush();
                }
            } catch (IOException e) {
                throw new RuntimeException("读取文件失败");
            } finally {
                try {
                    if(br != null) 
                        br.close();
                } catch (IOException e) {
                    throw new RuntimeException("读取流关闭失败");
                }
                try {
                    if(bw != null)
                        bw.close();
                } catch (IOException e) {
                    throw new RuntimeException("写入流关闭失败");
                }
            }
            
        }
    
    }

       异常的日志信息

     log4j:记录日志信息的一个工具。

       示例代码如下:

    import java.io.FileNotFoundException;
    import java.io.PrintStream;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class ExceptionInfo {
    
        public static void main(String[] args) {
            
            try {
                int[] arr = new int[2];
                System.out.println(arr[3]);
            } catch (Exception e) {
                try {
                    Date d = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String s = sdf.format(d);
                    PrintStream ps = new PrintStream("exception.log");
                    //ps.write(d.toString().getBytes());
                    ps.println(s);
                    System.setOut(ps);
                } catch (FileNotFoundException e1) {
                    throw new RuntimeException("日志文件创建失败!");
                }
                e.printStackTrace(System.out);
                //e.printStackTrace(new PrintStream("ex.txt"));
            }
        }
    
    }

       系统信息

       示例代码如下:

    import java.io.FileNotFoundException;
    import java.io.PrintStream;
    import java.util.Properties;
    
    public class SystemInfo {
    
        public static void main(String[] args) throws FileNotFoundException {
            Properties pro = System.getProperties();
            //System.out.println(pro);
            pro.list(new PrintStream("sysinfo.txt"));
        }
    
    }

       通过sysinfo.txt文本文件可以知道平台默认的字符集,即默认字符编码是GBK

       

  • 相关阅读:
    1105 Spiral Matrix (25分)(蛇形填数)
    1104 Sum of Number Segments (20分)(long double)
    1026 Table Tennis (30分)(模拟)
    1091 Acute Stroke (30分)(bfs,连通块个数统计)
    1095 Cars on Campus (30分)(排序)
    1098 Insertion or Heap Sort (25分)(堆排序和插入排序)
    堆以及堆排序详解
    1089 Insert or Merge (25分)
    1088 Rational Arithmetic (20分)(模拟)
    1086 Tree Traversals Again (25分)(树的重构与遍历)
  • 原文地址:https://www.cnblogs.com/yerenyuan/p/5269424.html
Copyright © 2011-2022 走看看