zoukankan      html  css  js  c++  java
  • 本地文件系统IO(Java)

    File对象

    • Java的标准库java.io提供了File对象来操作文件和目录
    • File f = new Flie("C:Windosa.txt")
    • 注意在字符串中表示一个
    • 用.表示当前目录,用..表示上级目录
    • getPath()返回构造方法传入的路径,getAbsolutePath(),返回绝对路径,一种是getCanonicalPath,它和绝对路径类似,但是返回的是规范路径
    • 构造一个File对象,并不会导致任何磁盘操作。只有当我们调用File对象的某些方法的时候,才真正进行磁盘操作
    import java.io.*;
    public class Main {
        public static void main(String[] args) throws IOException {
            File f1 = new File("C:\Windows");
            File f2 = new File("C:\Windows\notepad.exe");
            File f3 = new File("C:\Windows\nothing");
            System.out.println(f1.isFile());
            System.out.println(f1.isDirectory());
            System.out.println(f2.isFile());
            System.out.println(f2.isDirectory());
            System.out.println(f3.isFile());
            System.out.println(f3.isDirectory());
        }
    }
    
    • boolean canRead():是否可读;
    • boolean canWrite():是否可写;
    • boolean canExecute():是否可执行;
    • long length():文件字节大小。
    • File表示文件,可以创建和删除文件。表示目录时,也可以创建和删除目录
    • 可以使用list()和listFiles()列出目录下的文件和子目录名

    InputStream

    • 这是Java提供的最基本的输入流
    • InputSteam并不是一个接口,而是一个抽象类。他是所有输入流的超类,这个抽象类的一个重要方法 Int read()
    • FileInputStream是InputStream的一个子类。顾名思义,FileInputStream就是从文件流中读取数据。下面的代码演示了如何完整地读取一个FileInputStream的所有字节:
    public void readFile() throws IOException {
        // 创建一个FileInputStream对象:
        InputStream input = new FileInputStream("src/readme.txt");
        for (;;) {
            int n = input.read(); // 反复调用read()方法,直到返回-1
            if (n == -1) {
                break;
            }
            System.out.println(n); // 打印byte的值
        }
        input.close(); // 关闭流
    }
    
    • InputStream和OutputStream都是通过close()方法来关闭流。关闭流就会释放对应的底层资源
    • 如果读取过程中发生了IO错误,InputStream就没法正确地关闭,资源也就没法及时释放
    • 因此,我们需要用try ... finally来保证InputStream在无论是否发生IO错误的时候都能够正确地关闭:
    • 利用缓冲区一次性读取多个字节,效率高
    • 读取多个字节时,需要先定义一个byte[]数组作为缓冲区,read()方法会尽可能多地读取字节到缓冲区, 但不会超过缓冲区的大小。read()方法的返回值不再是字节的int值,而是返回实际读取了多少个字节。如果返回-1,表示没有更多的数据了
    public void readFile() throws IOException {
        try (InputStream input = new FileInputStream("src/readme.txt")) {
            // 定义1000个字节大小的缓冲区:
            byte[] buffer = new byte[1000];
            int n;
            while ((n = input.read(buffer)) != -1) { // 读取到缓冲区
                System.out.println("read " + n + " bytes.");
            }
        }
    }
    

    OutputStream

    • OutputSteam也是抽象类,是所有输出流的超类
    • 因为向磁盘、网络写入数据的时候,出于效率的考虑,操作系统并不是输出一个字节就立刻写入到文件或者发送到网络,而是把输出的字节先放到内存的一个缓冲区里(本质上就是一个byte[]数组),等到缓冲区写满了,再一次性写入文件或者网络。对于很多IO设备来说,一次写一个字节和一次写1000个字节,花费的时间几乎是完全一样的,所以OutputStream有个flush()方法,能强制把缓冲区内容输出。
    • 通常情况下,我们不需要调用这个flush()方法,因为缓冲区写满了OutputSteam会自动调用。
    public void writeFile() throws IOException {
        OutputStream output = new FileOutputStream("out/readme.txt");
        output.write("Hello".getBytes("UTF-8")); // Hello
        output.close();
    }
    

    Filter模式

    • ServletInputStream:从HTTP请求读取数据,是最终数据源;
    • Socket.getInputStream():从TCP连接读取数据,是最终数据源;
    • 通过一个“基础”组件再叠加各种“附加”功能组件的模式,称之为Filter模式(或者装饰器模式:Decorator)。它可以让我们通过少量的类来实现各种功能的组合
    • 下面的例子演示了如何编写一个CountInputStream,它的作用是对输入的字节进行计数:
    public class Main {
        public static void main(String[] args) throws IOException {
            byte[] data = "hello, world!".getBytes("UTF-8");
            try (CountInputStream input = new CountInputStream(new ByteArrayInputStream(data))) {
                int n;
                while ((n = input.read()) != -1) {
                    System.out.println((char)n);
                }
                System.out.println("Total read " + input.getBytesRead() + " bytes");
            }
        }
    }
    
    class CountInputStream extends FilterInputStream {
        private int count = 0;
    
        CountInputStream(InputStream in) {
            super(in);
        }
    
        public int getBytesRead() {
            return this.count;
        }
    
        public int read() throws IOException {
            int n = in.read();
            if (n != -1) {
                this.count ++;
            }
            return n;
        }
    
        public int read(byte[] b, int off, int len) throws IOException {
            int n = in.read(b, off, len);
            this.count += n;
            return n;
        }
    }
    

    操作zip

    • 创建一个ZipInputStream,通常是传入一个FileInputStream作为数据源,然后,循环调用getNextEntry(),直到返回null,表示zip流结束。
    • 一个ZipEntry表示一个压缩文件或目录,如果是压缩文件,我们就用read()方法不断读取,直到返回-1
    try (ZipInputStream zip = new ZipInputStream(new FileInputStream(...))) {
        ZipEntry entry = null;
        while ((entry = zip.getNextEntry()) != null) {
            String name = entry.getName();
            if (!entry.isDirectory()) {
                int n;
                while ((n = zip.read()) != -1) {
                    ...
                }
            }
        }
    }
    
    • ZipOutputStream是一种FilterOutputStream,它可以直接写入内容到zip包。我们要先创建一个ZipOutputStream,通常是包装一个FileOutputStream,然后,每写入一个文件前,先调用putNextEntry(),然后用write()写入byte[]数据,写入完毕后调用closeEntry()结束这个文件的打包。

    读取classpath资源

    • 把资源存储在classpath中可以避免文件路径依赖;
    • Class对象的getResourceAsStream()可以从classpath中读取指定资源;

    序列化

    • 序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]数组。
    • 为什么要把Java对象序列化呢?因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程,这样,就相当于把Java对象存储到文件或者通过网络传输出去了
    • 有序列化,就有反序列化,即把一个二进制内容(也就是byte[]数组)变回Java对象。有了反序列化,保存到文件中的byte[]数组又可以“变回”Java对象,或者从网络上读取byte[]并把它“变回”Java对象
    • Serializable接口没有定义任何方法,它是一个空接口。我们把这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。
    public class Main {
        public static void main(String[] args) throws IOException {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            try (ObjectOutputStream output = new ObjectOutputStream(buffer)) {
                // 写入int:
                output.writeInt(12345);
                // 写入String:
                output.writeUTF("Hello");
                // 写入Object:
                output.writeObject(Double.valueOf(123.456));
            }
            System.out.println(Arrays.toString(buffer.toByteArray()));
        }
    }
    
    
    • 和ObjectOutputStream相反,ObjectInputStream负责从一个字节流读取Java对象

    Reader

    • Reader是Java的IO库提供的另一个输入流接口。和InputStream的区别是,InputStream是一个字节流,即以byte为单位读取,而Reader是一个字符流,即以char为单位读取
    • 这个方法读取字符流的下一个字符,并返回字符表示的int,范围是0~65535。如果已读到末尾,返回-1
    • FileReader是Reader的一个子类,它可以打开文件并获取Reader。下面的代码演示了如何完整地读取一个FileReader的所有字符
    public void readFile() throws IOException {
        // 创建一个FileReader对象:
        Reader reader = new FileReader("src/readme.txt"); // 字符编码是???
        for (;;) {
            int n = reader.read(); // 反复调用read()方法,直到返回-1
            if (n == -1) {
                break;
            }
            System.out.println((char)n); // 打印char
        }
        reader.close(); // 关闭流
    }
    
    • 如果我们读取一个纯ASCII编码的文本文件,上述代码工作是没有问题的。但如果文件中包含中文,就会出现乱码,因为FileReader默认的编码与系统相关,例如,Windows系统的默认编码可能是GBK,打开一个UTF-8编码的文本文件就会出现乱码。
    • 要避免乱码问题,我们需要在创建FileReader时指定编码:
    • Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8);

    Writer

    • Reader是带编码转换器的InputStream,它把byte转换为char,而Writer就是带编码转换器的OutputStream,它把char转换为byte并输出。

    PrintStream和PrintWrite

    • 我们经常使用的System.out.println()实际上就是使用PrintStream打印各种数据。其中,System.out是系统默认提供的PrintStream,表示标准输出
    • PrintStream最终输出的总是byte数据,而PrintWriter则是扩展了Writer接口,它的print()/println()方法最终输出的是char数据。两者的使用方法几乎是一模一样的
  • 相关阅读:
    多个类定义attr属性重复的问题:Attribute "xxx" has already been defined
    好用的批量改名工具——文件批量改名工具V2.0 绿色版
    得到ImageView中drawable显示的区域的计算方法
    得到view坐标的各种方法
    实现类似于QQ空间相册的点击图片放大,再点后缩小回原来位置
    Material Designer的低版本兼容实现(五)—— ActivityOptionsCompat
    Android 自带图标库 android.R.drawable
    解决 Attempting to destroy the window while drawing!
    解决Using 1.7 requires compiling with Android 4.4 (KitKat); currently using API 4
    Material Designer的低版本兼容实现(四)—— ToolBar
  • 原文地址:https://www.cnblogs.com/chenshaowei/p/13194805.html
Copyright © 2011-2022 走看看