• 02 IO


    核心类

    对象想要被传输, 需要通过序列化来将对象编程“序列”.

    字符集对字符流的影响. 字符流底层还是字节流.

     分隔符

    public class IOTest {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            String path = "C:\JavaWork\eclipse-workspace\MyProject01\src\smart_production.JPG";
            String path2 = "C:/JavaWork/eclipse-workspace/MyProject01/src/smart_production.JPG";
            String path3 = "C:" + File.separator + "JavaWork" + File.separator + "eclipse-workspace" + File.separator + "MyProject01" +
                    File.separator + "src" + File.separator + "smart_production.JPG";
            System.out.println(path);
            System.out.println(path2);
            System.out.println(path3);
        }
        // 建议, path2 和 path3 都没有问题, 主要建议使用 path2 的方式
    }

    输出结果:

    遍历文件夹文件

    // 下级名称 list
            File dir = new File("C:/JavaWork/eclipse-workspace/MyProject01/src");
            String[] subNames = dir.list();
            for (String s:subNames) {
                System.out.println(s);
            }
            
            // 下级对象 listFiles()
            File[] subFiles = dir.listFiles();
            for (File s:subFiles) {
                System.out.println(s.getAbsolutePath());

    遍历所有的文件(子文件夹也遍历),  递归

            File src = new File("C:/JavaWork/eclipse-workspace/MyProject01/src");
            printName(src, 0);
    
        }
            // 用递归, 非常占用内存
            public static void printName(File src, int deep) {
                System.out.println(src.getName() + " " + deep);
                if (null == src || !src.exists()) {
                    return;
                } else if (src.isDirectory()) {
                    for (File s:src.listFiles()) {
                        printName(s, deep+1);
                    }
                }
            }

    字符集 & 乱码

    编码: 由字符 -> 字节(2进制), encode

    解码: 由字节 -> 字符(人能读懂), decode (计算机只认识 0低电平,1高电平 等字节)

    "a" 有个代号整数比如64,然后转换成 2进制数, 就编码了(转换成字节码了).  所以这个代号的集合就是字符集. 同样 "中"这个字也是可以利用字符集来编码.

    // 编码: 字节数组
            byte[] datas = msg.getBytes();    // 默认使用工程的字符集
            System.out.println(datas.length);
            // 解码: 字符串 
            msg = new String(datas, 0, datas.length, "UTF-8");

    解码的过程中可能产生乱码:

      因为长度不够, (如果采用一个一个字节的方式读取, 就可能产生乱码, 因为比如汉子, 它是多字节的.)

      字符集跟之前不匹配.

    字节流: 处理视频, 音频, word, excel 等. 

    字符流: 比如处理纯文本, 也就是说文件本身就是字符的, 那么,我们不用编码成字节了, 可以直接处理.

     JAVA虚拟机本身无权操作文件, 是借助操作系统来完成的对文件的操作.

    常用类及方法

    字节流

    标准步骤

    1. 确定源

    2. 选择流

    3. 操作(读,写)

    4. 释放资源

    package com.pratice.java300;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    
    /*
     * 1. 创建源
     * 2. 选择流
     * 3. 操作(读写)
     * 4. 释放资源
     */
    public class IO02 {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            // 1.创建源
            File src = new File("stanford.txt");
            InputStream is = null;
            try {
                // 2.选择流
                is = new FileInputStream(src);
                int temp = 0;
                // 3.开始处理, 文件读取完, 返回-1
                while ((temp = is.read()) != -1) {
                    System.out.println((char)temp);
                }    
            } catch(FileNotFoundException e) {
                e.printStackTrace();
            } catch(IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    if (null != is) {
                        // 4.关闭文件
                        is.close();
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }     
        }
    }

    上例中, 每次读取1个字节, 也可以一次多字节读取.

    package com.pratice.java300;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class IOTest04 {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            File src = new File("stanford.txt");
            InputStream is = null;
            try {
                is = new FileInputStream(src);
                int len = -1;
                // 每3个字节, 3个字节读取, 一次性读取的大小, 如果写100,那对于本例子等于一次性读取完
                byte[] car = new byte[3];
                while (null != is &&
                        (len = is.read(car)) != -1) {
                    String str = new String(car, 0, len);
                    System.out.println(str);
                }
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != is) {
                        is.close();
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }   
        }
    }

     输出流, 注意输出字符, 要转换成字节数组来跟文件交互. (也是这4步骤)

    package com.pratice.java300;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    
    public class IOTest05 {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            // 1. 创建源
            File src = new File("asdf.txt");    //如果文件不存在, 自动创建
            // 2. 选择流
            OutputStream os = null;
            try {
                os = new FileOutputStream(src, false);  //false 覆盖文件, true 追加文件
                // 3. 操作写出
                String str = "IO is so easy.";
                byte[] dates = str.getBytes();    // 因为是操作字节, 所以这里要转换成字节
                os.write(dates);
                os.flush();  // 避免数据驻留在内存中
            }catch(FileNotFoundException e) {
                e.printStackTrace();
            }catch(IOException e) {
                e.printStackTrace();
            }finally {
                if (null != os) {
                    try {
                        // 4.关闭文件
                        os.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    文件 copy, 只需要将上边的代码合二为一就可以了.

    package com.pratice.java300;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    // 实现文件copy
    public class IOTest05 {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            // 1. 创建源
            File src = new File("stanford.txt");    //输入文件
            File dest = new File("dest.txt");    // 输出文件, 如果文件不存在, 自动创建
            // 2. 选择流
            InputStream is = null;
            OutputStream os = null;
            try {
                is = new FileInputStream(src);
                os = new FileOutputStream(dest, true);
                int len = -1;
                // 3. 操作, 每次copy 3 字节进入目标文件
                byte[] dates = new byte[3];
                while (null != is && null != os
                        && (len = is.read(dates)) != -1) {
                    os.write(dates);  // 这应该使用 os.write(dates, 0, len);
                    os.flush();  // 避免数据驻留在内存中
                }
            }catch(FileNotFoundException e) {
                e.printStackTrace();
            }catch(IOException e) {
                e.printStackTrace();
            }finally {
                if (null != os && null != is) {
                    try {
                        // 4.关闭文件
                        is.close();
                        os.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }

    源文件是:

    CS106A

    CS107B

    但是 copy 出来的文件是: (运行结果)

    CS106A

    CS106B

    B

    为什么呢?

    因为上面的操作, 实际上是每次读取源文件, 把字节放到字节数组date中, 而实际程序运行时, 每次读取文件时, date数组实际上还还保存着上次读取文件的结果, (实际上每行的最后都有两个字节:换行, 回车)可是到了最后一次读取就出问题, 每次读取三个, 我们分配一下 共16个字符(12原本字符 + 4个(换行回车))

    CS1

    06A

    换行回车C

    S10

    6B换行

    回车B换行

    所以这个粉色的B换行不是这次文件读取到的, 而是原来保存在 dates 数组中的. 所以这个会再次copy到 dest 文件, 这是有问题的.

    所以, 如果想使用批量的读取, 那么也有解决办法,解决办法就是写文件使用 os.write(dates, 0, len);  这样读到最后, len 的长度是1, 所以只写一个元素.

    文件流

    通过字符的方式处理文件. 对比于字节数组, 这里的处理结果保存在字符数组中.

    流程都一样, 都是那4步骤

    文件字符输入流
    package com.pratice.java300;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.Reader;
    
    public class IOTest6 {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            File src = new File("stanford.txt");
            Reader reader = null;
            try {
                reader = new FileReader(src);
                int len = -1;
                // 每3个字符
                char[] car = new char[3];
                while (null != reader &&
                        (len = reader.read(car)) != -1) {
                    String str = new String(car, 0, len);    // 同样要注意文件最后的长度问题
                    System.out.print(str);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != reader) {
                        reader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }    
        }
    }
    文件字符输出流, 实际上都是按照字节来修改一下
    package com.pratice.java300;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Writer;
    
    public class IOTest7 {
    
        public static void main(String[] args) {
            // 1. 创建源
            File dest = new File("dest.txt");    //输入文件
            // 2. 选择流
            Writer writer = null;
            try {
                writer = new FileWriter(dest, true);
                // 3. 操作
                String msg = "IO 你好 asdf kkk";
                char[] dates = msg.toCharArray();    //转换成字符数组
                writer.write(dates, 0, dates.length);
           writer.write(msg) // 直接丢就可以.
    writer.append(msg) // 可以累加, 而且可以多次写.
    writer.flush();
    // 避免数据驻留在内存中 }catch(FileNotFoundException e) { e.printStackTrace(); }catch(IOException e) { e.printStackTrace(); }finally { if (null != writer) { try { // 4.关闭文件 writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } }

    字节数组流 ByteArrayInputStream & ByteArrayOutputStream

    源文件是一块内存, Java 可以直接访问.  这种在内存中的数组流, 不用关闭.(因为是在内存中) 由 GC 帮我们释放内存.

    所有的东西, 都可以转成字节数组流, 因为是2进制的, 所以字节数组流方便我们进行网络传输.

    流程上, 还是那4步骤.

    package com.pratice.java300;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class IOTest8 {
    
        public static void main(String[] args) {
            // 创建源
            byte[] src = "asdffdaxxx".getBytes();
            InputStream is = null;    // 多肽, 为了更加通用
            try {
                is = new ByteArrayInputStream(src);
                int len = -1;
                // 每5字节, 5个字节读取
                byte[] flush = new byte[5];
                while (null != is &&
                        (len = is.read(flush)) != -1) {
                    String str = new String(flush, 0, len);
                    System.out.println(str);
                }
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    ByteArrayOutputStream 本身不需要输出到文件(是输出到内存中的流, 也就是一个新的字节数组), 所以同样也不需要关闭文件.

    但是, 你有关闭代码也无所谓.

    package com.pratice.java300;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    
    public class IOTest9 {
    
        public static void main(String[] args) {
            // 1. 创建源
            byte[] dest = null;
            // 2. 选择流(新增方法)
            ByteArrayOutputStream baos = null;
            try {
                baos = new ByteArrayOutputStream();
                // 3. 操作
                String msg = "IO 你好 asdf kkk";
                byte[] datas = msg.getBytes();
                baos.write(datas, 0, datas.length); // 写到内存中
                baos.flush();  // 避免数据驻留在内存中
                // 获取数据
                dest = baos.toByteArray();
                System.out.println(dest.length + "-->" + new String(dest, 0, baos.size()));
            }catch(IOException e) {
                e.printStackTrace();
            }finally {
                if (null != baos) {
                    try {
                        // 4.关闭文件
                        baos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }

     

    将图片 copy 成字节数组流 -> copy 到其他的文件. (这样可以实现图片的 copy) 

    所以, IO 操作都是 流对流.

    package com.pratice.java300;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.Closeable;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    /*
     * 1. 封装拷贝
     * 2. 封装释放资源
     */
    
    public class FileUtils {
    
        public static void main(String[] args) {
            // 文件到文件
            try {
                InputStream is = new FileInputStream("stanford.txt");
                OutputStream os = new FileOutputStream("oo.txt");
                copy(is, os);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // 文件到字节数组
            byte[] datas = null;
            try {
                InputStream is = new FileInputStream("plane.jpg");
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                copy(is, os);
                datas = os.toByteArray();
                System.out.println(datas.length);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // 字节数组到文件
            try {
                InputStream is = new ByteArrayInputStream(datas);
                OutputStream os = new FileOutputStream("p-copy.jpg");
                copy(is, os);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        public static void copy(InputStream is, OutputStream os) {
            try {
                int len = -1;
                byte[] flush = new byte[5];
                while (null != is &&
                        (len = is.read(flush)) != -1) {
                    os.write(flush, 0, len);
                }
            }catch (IOException e) {
                e.printStackTrace();
            }finally {
    //            close(is, os);
                close2(is, os);
            }
        }
        public static void close(InputStream is, OutputStream os) {
            try {
                if (null != os) {
                    os.close();
                }
                if (null != is) {
                    is.close();
                }
            }catch(IOException e) {
                e.printStackTrace();
            }
        }
        // 释放资源, Closeable... 是可变参数的意思
        public static void close2(Closeable... ios) {
            for(Closeable io:ios) {
                try {
                    if (null != io) {
                        io.close();
                    }
                }catch(IOException e) {
                    e.printStackTrace();
                }
            }
        }
        // try.with.resource 可以自动释放资源.
        public static void copy2(String srcPath, String destPath) {
            // 1. 创建源
            File src = new File(srcPath);
            File dest = new File(destPath);
            // 2. 选择流, 自动释放资源就是在 try括号内部的这个 is, os
            try(InputStream is = new FileInputStream(src);
                OutputStream os = new FileOutputStream(dest);) {
                int len = -1;
                byte[] flush = new byte[5];
                while (null != is &&
                        (len = is.read(flush)) != -1) {
                    os.write(flush, 0, len);
                }
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    BufferedInputStream 和 BufferedOutputStream 提高IO 性能.  把读写维护了一个缓冲区buffer, 当buffer满了之后,再调用IO 操作. 这样可以介绍IO操作.

     默认的这个 buffer 是 8k, 我们可以先不考虑修改这个大小.

    释放时,只需要释放最外层的流,它内部的,它会自己释放. 

    下边代码, 可以看到,在字节流上直接套上buffer就可以了, 后面的处理方法都是完全一样的.

    package com.pratice.java300;
    
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class IOTest04 {
    
        public static void main(String[] args) {
            // 创建源
            File src = new File("stanford.txt");
            InputStream is = null;
            try {
                is = new BufferedInputStream(new FileInputStream(src));
                int len = -1;
                byte[] car = new byte[1024];
                while (null != is &&
                        (len = is.read(car)) != -1) {
                    String str = new String(car, 0, len);
                    System.out.println(str);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != is) {
                        is.close();    
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }   
        }
    }

    输出流也是一样的,直接套上字节缓冲流就可以了,所以copy函数, 也只是将try()里的声明加上字节缓冲流就可以了.

    同样, 也有字符缓冲流. BufferedReader, BufferedWriter

    逐行读取, 读取不到就返回空 null.

    package com.pratice.java300;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class BufferCopyTest {
    
        public static void main(String[] args) {
            copy("stanford.txt", "xxx.txt");
        }
        
        public static void copy(String srcPath, String destPath) {
            File src = new File(srcPath);
            File dest = new File(destPath);
            try(BufferedReader br = new BufferedReader(new FileReader(src));
                BufferedWriter bw = new BufferedWriter(new FileWriter(dest));) {
                String line = null;
                int lineCount = 0;
                while ((line = br.readLine()) != null) {
                    if (lineCount == 0) {
                        lineCount++;
                    } else {
                        bw.newLine();    // 这种换行方式, 不会在文件末尾增加新行
                    }
                    bw.write(line);
    //                bw.newLine();    // 实际上, 这样操作在文件最后会多一个换行回车
                }
                bw.flush();
            }catch(IOException e) {
                e.printStackTrace();
            }
        }
    }

    字节流转换为字符流

    如果是纯文本的文件, 而且给的是字节流,那么转换成字符流更加方便. (注意字符集)

    InputStreamReader 读取字节 -> 转换成字符(解码, 根据字符集来解码)

    OutputStreamWriter: 读取字符 -> 字节 (编码)

    写的方法基本一样, 这里copy 一下得了.

    读取网页作为字节流, 按照UTF-8 字符集转换为字符流, 将读取的网页写到 baidu.html 文件里.

    package com.pratice.java300;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.URL;
    
    public class BufferWeb {
    
        public static void main(String[] args) {
            convertWeb();
        }
    
        public static void convertWeb() {
            try(BufferedReader reader = 
                new BufferedReader(    // 套这层, 用buffer
                    new InputStreamReader(    // 套这层: 字节流 -> 字符流
                        new URL("http://www.baidu.com").openStream(), "UTF-8"));
                BufferedWriter writer = 
                new BufferedWriter(    // 套这层, 用buffer
                    new OutputStreamWriter(    // 套这层: 字符流 -> 字节流
                        new FileOutputStream("baidu.html"), "UTF-8"));){    // 字节输出流到文件
                String msg = null;
                while ((msg = reader.readLine()) != null) {
                    writer.write(msg);
                    writer.newLine();
                }
                writer.flush();
            }catch(IOException e) {
                e.printStackTrace();
            }
        }
    }

    数据流 (保留了数据类型)

    比如可以 readInt, 或者可以 writeInt 之类的. writeUTF("asdffda")

     

     而且这个 baos 和 datas 都可以加 buffer 来增加性能

    对象流

    序列化(也叫持久化) 和 反序列化 (不是所有的对象都可以序列化), 想序列化必须实现序列化接口.

    跟数据流代码很像. 

    打印流 (System.out 就是用这个实现的)

    PrintStream ps = System.out;

    CommonsIO

    会有很多的 commons 的库. 一般公司有自己的commons库.

    多个 jar 包 组成组件, 多个组件component 就组成框架了. CommonsIO 就是一个组件. CommonsIO 是 apache 基金会提供的组件.

    可以通过 apache.org 看到提供的组件列表. 比如 kafka, hadoop 等

    很多时候直接使用这个功能就好,代码都写好了.

    CommonsIO 里边有 FileUtils

    引用的办法:首先下载 window版的, 然后解压, 把 commons-io-2.6.jar 和 common-io-2.6-sources.jar(源码) copy 到项目里(新建一个libe文件夹)

    解压后, 目录里有一个 docs 可以看到说明文档.

    在 lib 文件夹中现在就有了这两个文件, 选中 commons-io-2.6.jar 然后右键 -> Build Path ->(Add to Build Path) -> 这样就自动进入 Referenced Libraries中了, 就可以直接用了.

    想关联我们的源码时, 当点击鼠标想看对象时, 当出现如下图时, 点 Attach Source.. 就是关联源码

    在这可以直接选择 work_plack , 因为刚刚你已经把源码 copy 到了lib 目录下.

    总结

    文件主要是流,从三个角度说

    1)输入流,输出流

    2)字节流,字符流, 字节和字符的转换流

    3) 装饰流, 为了提高性能(buffer)

    4) data 流

    5) object 流, 序列化 -> 反序列

    重点掌握 文件 copy.

    固定套路(顺序步骤): 1,2,3,4

    工作中主要还是使用 commonsIO 的. (我们写原生的代码还是比较少的, 这种类似的公共组件, 主要是借助知名公司的公共组件)

  • 相关阅读:
    eclipse恢复界面默认设置
    文件夹的拷贝
    文件的输入输出
    十进制转二进制,八进制,十六进制(查表法)
    数组元素的查找(折半查找)
    C++几个小函数
    C++ 内部排序(一)
    C++实现链表
    C++输出IP地址段内的合法地址
    我看软件工程师的职业规划
  • 原文地址:https://www.cnblogs.com/moveofgod/p/12436718.html
Copyright © 2020-2023  润新知