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数据。两者的使用方法几乎是一模一样的
  • 相关阅读:
    android编译全过程
    Android APK反编译得到Java源代码和资源文件
    获取Android的Java源代码并在Eclipse中关联查看的最新方法《转载》
    定制ROM,添加apk文件报错。
    Ubuntu下下载编译android源码
    poj 2714 Random Walk
    hdu 3829 Cat VS Dog
    第九场组队赛总结
    2012 MUTC 9 总结
    hdu 1100 Trees Made to Order
  • 原文地址:https://www.cnblogs.com/chenshaowei/p/13194805.html
Copyright © 2011-2022 走看看