• Java的基本使用之IO


    1、IO的基本介绍

    IO是指 Input/Output,即输入和输出。以内存为中心:

    • Input 指从外部读入数据到内存,例如把文件从磁盘读取到内存,从网络读取数据到内存等等。

    • Output 指把数据从内存输出到外部,例如把数据从内存写入到文件,把数据从内存输出到网络等等。

    Java 代码是在内存中运行的,所以数据也必须读到内存才能对其进行处理,数据在 Java 代码中的最终表示方式无非是 byte数组,字符串等这些 Java 数据类型,都必须存放在内存里。

    从 Java 代码来看,输入实际上就是从外部,例如硬盘上的某个文件,把内容读到内存,并且以 Java 提供的某种数据类型表示,例如,byte[]String,这样,后续代码才能处理这些数据。因为内存有“易失性”的特点,所以必须把处理后的数据以某种方式输出,例如,写入到文件。Output 实际上就是把 Java 表示的数据格式,例如,byte[]String等输出到某个地方。

    IO 流是一种顺序读写数据的模式,它的特点是单向流动。数据类似自来水一样在水管中流动,所以我们把它称为IO流。IO流以byte(字节)为最小单位,因此也称为字节流。

    1.1、流的分类

    按操作数据单位不同分为:字节流、字符流

    按数据流的流向不同分为:输入流、输出流

    按流的角色不同分为:节点流、处理流

     Java 的 IO 流共涉及40多个类,但都是从 InputStream、OutputStream、Reader、Writer这四个抽象基类派生的。由这四个类派生出来的子类的名称都以其父类名作为后缀。

    1.2、输入/输出字节流(InputStream / OutputStream)

    IO 流以 byte(字节)为最小单位,因此也称为字节流。 在Java中,InputStream代表输入字节流,OuputStream代表输出字节流,这是最基本的两种IO流。

    例如,我们要从磁盘读入一个文件,包含6个字节,就相当于读入了6个字节的数据。

    这6个字节是按顺序读入的,所以是输入字节流。

    反过来,我们把6个字节从内存写入磁盘文件,就是输出字节流:

    1.3、字符流(Reader / Writer)

    如果我们需要读写的是字符,并且字符不全是单字节表示的 ASCII 字符,那么,按照字符 char 来读写显然更方便,这种流称为字符流。

    Java提供了ReaderWriter表示字符流,字符流传输的最小数据单位是char

    例如,我们把 char[] 数组 “Hi你好” 这4个字符用Writer字符流写入文件,并且使用UTF-8编码,得到的最终文件内容是8个字节,英文字符 H 和 i 各占一个字节,中文字符 “你好” 各占3个字节。

    反过来,我们用Reader读取以UTF-8编码的这8个字节,会从Reader中得到Hi你好这4个字符。

    因此,ReaderWriter本质上是一个能自动编解码的InputStreamOutputStream

    使用Reader,数据源虽然是字节,但我们读入的数据都是char类型的字符,原因是Reader内部把读入的byte做了解码,转换成了char。使用InputStream,我们读入的数据和原始二进制数据一模一样,是byte[]数组,但是我们可以自己把二进制byte[]数组按照某种编码转换为字符串。

    究竟使用Reader还是InputStream,要取决于具体的使用场景。如果数据源不是文本,就只能使用InputStream,如果数据源是文本,使用Reader更方便一些。WriterOutputStream是类似的。

    1.4、同步和异步IO

    同步IO是指,读写IO时代码必须等待数据返回后才继续执行后续代码,它的优点是代码编写简单,缺点是CPU执行效率低。

    而异步IO是指,读写IO时仅发出请求,然后立刻执行后续代码,它的优点是CPU执行效率高,缺点是代码编写复杂。

    Java标准库的包java.io提供了同步IO,InputStreamOutputStreamReaderWriter都是同步IO的抽象类,对应的具体实现类,以文件为例,有FileInputStreamFileOutputStreamFileReaderFileWriter

    java.nio则是异步IO。

    2、File 对象

    Java的标准库java.io提供了File对象来操作文件和目录。File对象既可以表示文件,也可以表示目录。File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身,即只能操作文件或者目录,但不能操作文件内容本身。如果需要访问文件内容本身,需使用输入/输出流。

    2.1、构造File对象(new File())

    要构造一个File对象,需要传入文件路径:

    import java.io.*;
    public class Main {
        public static void main(String[] args) {
            File f = new File("C:\Windows\notepad.exe");
            System.out.println(f);
        }
    }

    构造File对象时,既可以传入绝对路径,也可以传入相对路径。File 对象中传入的相对路径是相对于你当前 Java 文件所处的项目的根目录而言的,而不是相对于当前 Java 文件的路径。

    (可以使用 或者 / 作为路径分隔符。因为 在Java中是转义字符,所以使用 作为分隔符时需使用两个 即 \ )

    //绝对路径:以根目录开头的完整路径
    File f = new File("C:\Windows\notepad.exe");
    File f = new File("C:/Windows/notepad.exe");
    
    //传入相对路径时,相对路径前面加上当前项目的根目录就是绝对路径。比如下面当前目录是C:Docs
    File f1 = new File("sub\javac"); // 则绝对路径是C:Docssubjavac
    File f3 = new File(".\sub\javac"); // 绝对路径是C:Docssubjavac
    File f3 = new File("..\sub\javac"); // 绝对路径是C:subjavac

    2.2、创建和删除文件(createNewFile()、delete())

    当File对象表示一个文件时,可以通过 File 对象的 createNewFile() 方法来创建一个新文件,如果创建成功,该方法将返回 true,如果该文件已经存在则返回 false,如果指定的目录不存在将会抛出异常,不会自动创建目录。

    //比如当前的项目根目录为F:JavaSE_WorkSpacedemo01
    File file = new File(".\path\file.txt");  //F:JavaSE_WorkSpacedemo01pathfile.txt
    
    if (file.createNewFile()) {   //如果文件的目录不存在将会报错
        System.out.println("创建成功");
    }

    可以用 delete() 方法来删除一个文件,当删除成功时返回 true,否则返回 false,该方法不会因为没有找到目录而报错

    File file = new File(".\path\file.txt");
    
    if (file.delete()) {
        System.out.println("删除成功");
    }

    有些时候,程序需要读写一些临时文件,File对象提供了createTempFile()来创建一个临时文件,该方法将会在默认的临时文件目录下创建文件,比如:C:Users张三AppDataLocalTemp 目录下。

    可以用deleteOnExit()在JVM退出时自动删除该临时文件。

    File f = File.createTempFile("tmp-", ".txt"); // 提供临时文件的前缀和后缀
    //f.deleteOnExit();    // 该方法将会在JVM退出时自动删除该文件

    2.3、创建目录

    和文件操作类似,File对象如果表示一个目录,可以通过以下方法创建和删除目录:

    • boolean mkdir():创建当前File对象表示的目录。只能创建一层目录,如果父级不存在需要一层层调用先创建父级再创建子级目录
    • boolean mkdirs():创建当前File对象表示的目录。该方法可以创建多层,即在必要时会自动将不存在的父目录也创建出来;
    • boolean delete():删除当前File对象表示的目录,当前目录必须为空才能删除成功。

    2.4、遍历文件和目录(list()、listFiles())

    当File对象表示一个目录时,可以使用list()listFiles()列出目录下的文件和子目录名。

    list() 方法返回目录下的文件或者子目录的名称字符串数组。listFiles() 方法返回目录下的文件或者子目录的 File 对象数组,listFiles() 提供了一系列重载方法,可以过滤不想要的文件和目录。

    public static void main(String[] args) throws IOException {
        File f = new File("F:\JavaSE_WorkSpace2\day01");
        File[] fs1 = f.listFiles(); // 得到该目录下的所有文件和子目录
        printFiles(fs1);             
    
        File[] fs2 = f.listFiles(new FilenameFilter() {  //限制仅列出.exe文件
            public boolean accept(File dir, String name) {
                return name.endsWith(".exe"); // 返回true表示接受该文件
            }
        });
        printFiles(fs2);
    }
    
    //封装一个循环遍历函数
    static void printFiles(File[] files) {
        if (files != null) {
            for (File f : files) {
                System.out.println(f);
            }
        }
        System.out.println("遍历结束");
    }

    2.5、File对象的一些其他方法

    File对象既可以表示文件,也可以表示目录。特别要注意的是,构造一个File对象,即使传入的文件或目录不存在,代码也不会出错,因为构造一个File对象,并不会导致任何磁盘操作。只有当我们调用File对象的某些方法的时候,才真正进行磁盘操作。

    例如,调用isFile(),判断该File对象是否是一个已存在的文件,调用isDirectory(),判断该File对象是否是一个已存在的目录。

    File对象获取到一个文件时,还可以进一步判断文件的权限和大小:

    • boolean canRead():是否可读;
    • boolean canWrite():是否可写;
    • boolean canExecute():是否可执行;
    • long length():文件字节大小。

    对目录而言,是否可执行表示能否列出它包含的文件和子目录。

    File f1 = new File("C:\Windows");
    System.out.println(f1.isFile());          //false
    System.out.println(f1.isDirectory());    //true

     

    2.6、三种类型的路径(绝对路径、相对路径、规范路径)

    File 对象有三种形式表示的路径,包括绝对路径、相对路径和规范路径,规范路径就是去掉相对路径中的 . 和 .. 符号,得出一个绝对路径。

    请注意,File 对象中传入的相对路径是相对于你当前 Java 文件所处的项目的根目录而言的,而不是相对于当前 Java 文件的路径。

    //比如目前的Java文件存储在 E:workspacedemo01 项目的 default 包下
    File f = new File(".\path\file.txt");
    System.out.println(f.getPath());           //获取创建File对象时传入的路径  .pathfile3.txt
    System.out.println(f.getAbsolutePath());   //获取绝对路径   E:workspacedemo01.pathfile3.txt
    System.out.println(f.getCanonicalPath());  //获取规范路径  E:workspacedemo01pathfile3.txt

    3、输入字节流(InputStream)

    InputStream 就是 Java 标准库提供的最基本的输入流。它位于 java.io 这个包里。java.io 包提供了所有同步IO的功能。

    InputStream 并不是一个接口,而是一个抽象类,它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是 int read(),签名如下:

    //该方法会读取输入流的下一个字节,并返回字节表示的int值(0~255,一个字节的十进制范围就是0~255)。如果已读到末尾,返回-1表示不能继续读取了。
    public abstract int read() throws IOException;

    read() 方法是阻塞(Blocking)的,在调用InputStreamread()方法读取数据时,必须等到 read() 方法返回后才会继续执行下一段代码。

    3.1、读取文件(文件输入字节流 FileInputStream)

    FileInputStreamInputStream的一个子类。顾名思义,FileInputStream就是从文件流中读取数据。下面的代码演示了如何完整地读取一个FileInputStream的所有字节:

    public void readFile() throws IOException {   //抛出IO异常
        InputStream input = null;
        try {
            input = new FileInputStream("src/readme.txt");
            int n;
            while ((n = input.read()) != -1) { // 利用while同时读取并判断
                System.out.println(n);
            }
        } finally {   //在finally里保证InputStream一定能关闭
            if (input != null) { input.close(); }  //通过close()方法来关闭流
        }
    }

    在计算机中,类似文件、网络端口这些资源,都是由操作系统统一管理的。应用程序在运行的过程中,如果打开了一个文件进行读写,完成后要及时地关闭,以便让操作系统把资源释放掉。否则,应用程序占用的资源会越来越多,不但白白占用内存,还会影响其他应用程序的运行。

    InputStreamOutputStream都是通过close()方法来关闭流。关闭流就会释放对应的底层资源。

    在读取或写入IO流的过程中,可能会发生错误,例如,文件不存在导致无法读取,没有写权限导致写入失败,等等,这些底层错误由Java虚拟机自动封装成IOException异常并抛出。因此,所有与IO操作相关的代码都必须正确处理IOException。我们可以在 finally 里面调用 close 来保证不管 InputStream 是否发生错误都能正常关闭。

    我们可以用Java 7引入的新的try(resource)的语法来让编译器自动关闭资源。只需要编写try语句,编译器就会自动查看看try(resource = ...)中的对象是否实现了java.lang.AutoCloseable接口,如果实现了,就自动加上finally语句并调用close()方法为我们关闭资源。

    public void readFile() throws IOException {
        try (InputStream input = new FileInputStream("src/readme.txt")) {
            int n;
            while ((n = input.read()) != -1) {
                System.out.println(n);
            }
        } // 编译器会在此自动为我们写入finally并调用close()
    }

    3.2、利用缓冲区读取文件

    在读取流的时候,一次读取一个字节并不是最高效的方法。很多流支持一次性读取多个字节到缓冲区,对于文件和网络流来说,利用缓冲区一次性读取多个字节效率往往要高很多。InputStream提供了两个重载方法来支持读取多个字节:

    • int read(byte[] b):读取若干字节并填充到byte[]数组,返回一次性读取的字节数
    • int read(byte[] b, int off, int len):指定byte[]数组的偏移量和最大填充数

    利用上述方法一次读取多个字节时,需要先定义一个byte[]数组作为缓冲区,read()方法会尽可能多地读取字节到缓冲区, 但不会超过缓冲区的大小。read()方法的返回值不再是字节的int值,而是返回实际读取了多少个字节。如果返回-1,表示没有更多的数据了。

    public void readFile() throws IOException {
        try (InputStream input = new FileInputStream("src/readme.txt")) {
            // 定义1000个字节大小的缓冲区。(如果定义的缓冲区字节数组太小,则读出来可能有乱码。比如缓冲区是10个字节,一个中文是2-4个字节,一次性读不完则可能只读到某个中文的字节数的一半导致出现乱码)
            byte[] buffer = new byte[1000];
            int n;
            while ((n = input.read(buffer)) != -1) { // 读取到缓冲区
                System.out.println("一次性读取了" + n + "个字节");
                System.out.println(new String(buffer, 0, n));   //输出字符串
            }
        }
    }

    4、输出字节流(OutputStream)

    OutputStream是Java标准库提供的最基本的输出流,OutputStream也是抽象类,它是所有输出流的超类。这个抽象类定义的一个最重要的方法就是void write(int b)

    //该方法会根据参数的十进制的值表示的字节来写入
    public abstract void write(int b) throws IOException;

    InputStream类似,OutputStream也提供了close()方法关闭输出流,以便释放系统资源。

    OutputStream还提供了一个flush()方法,该方法能将缓冲区的内容进行输出。在缓冲区写满了时,OutputStream会自动调用该方法。在调用close()方法关闭OutputStream之前,也会自动调用flush()方法。当然,我们也可以主动去调用该方法。

    4.1、将字节写入文件(文件输出字节流 FileOutputStream)

    我们可以调用 write(int n) 方法来将十进制 n 代表的字节写入某个文件当中。当该文件不存在时,该方法会自动创建一个文件并且写入。当文件已经存在时,该方法会创建一个新的同名文件进行覆盖并写入。当目录不存在时会报错。

    public void writeFile() throws IOException {
        OutputStream output = new FileOutputStream("out/readme.txt");
        output.write(72); // H
        output.write(101); // e
        output.write(108); // l
        output.write(108); // l
        output.write(111); // o
        output.flush();  //write方法只是将数据写到内存中,该方法会将内存中的数据写到硬盘中。可以不手动调用该方法,因为在调用close()时也会自动调用flush()方法
        output.close();  
    }

    我们可以使用 write(byte[] arr)  方法来一次写入多个字节:

    public void writeFile() throws IOException {
        try (OutputStream output = new FileOutputStream("out/readme.txt")) {
            output.write("Hello".getBytes("UTF-8")); 
            output.write("你好啊,你是哪位".getBytes("UTF-8"));
        } // 编译器会自动在此为我们写入finally并调用close()
    }

    InputStream的read()方法一样,OutputStreamwrite()方法也是阻塞的,即必须等到这些方法返回后才会继续执行下一段代码。

    4.2、将某个文件的内容复制到另一个文件

    文件的字节流是非常通用的,因为字节流直接使用的是二进制。文件的字节流不仅可以用来操作文档,还可以用来操作任何的其他类型的文件比如图片、压缩包等等。所以下面的方法对于其他类型的文件也是通用的,将路径和文件名修改即可。

    File fileIN = new File("d:/TEST/MyFile2.txt"); //定义输入文件
    File fileOUT = new File("d:/TEST/MyFile3.txt"); //定义输出文件
       
    FileInputStream fis = null;
    FileOutputStream fos = null;
       
    try {    
       fis = new FileInputStream(fileIN); //输入流连接到输入文件
       fos = new FileOutputStream(fileOUT); //输出流连接到输出文件
        
       byte[] arr = new byte[10]; //该数组用来存入从输入文件中读取到的数据
       int len; //变量len用来存储每次读取数据后的返回值    
       while( ( len=fis.read(arr) ) != -1 ) {
          fos.write(arr, 0, len);
       }//while循环:每次从输入文件读取数据后,都写入到输出文件中
    } catch (IOException e) { e.printStackTrace(); } //关闭流 try { fis.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); }

    5、输入字符流(Reader)

    Reader是Java的IO库提供的另一个输入流接口,他以字符 char 为单位进行读取。

    字节输入流InputStream和字符输入流Reader的区别:

     

    java.io.Reader是所有字符输入流的超类,它最主要的方法是 read(),该方法读取字符流的下一个字符,并返回字符表示的int,范围是0~65535。如果已读到末尾,则返回-1。

    public int read() throws IOException;

    (读取文本文件通常使用字符流,而像视频、图片、音频等文件都是二进制数据,需要使用字节流读取。当然文本文件也是可以通过字节流来读取和写入的。字节流更通用,字符流只不过是对字节流进行了封装,查表操作)

    5.1、文件输入字符流(FileReader)

    FileReaderReader的一个子类,它可以打开文件并获取Reader。

    下面代码示例一个一个字符读取文件:

    public void readFile() throws IOException {
        // 创建一个FileReader对象:
        Reader reader = new FileReader("src/readme.txt");
        for (;;) {
            int n = reader.read(); // 反复调用read()方法,直到返回-1
            if (n == -1) {  //n为-1表示已经读到末尾
                break;
            }
            System.out.println((char)n); // 分别打印出读取出的单个字符
        }
        reader.close(); // 关闭流
    }

    Reader还提供了一次性读取若干字符并填充到char[]数组的方法:read(char[] c),此时它返回的是一次实际读入的字符个数,最大不能超过char[] c字符数组的长度

    public int read(char[] c) throws IOException   //该方法返回的是一次实际读入的字符个数,最大不会超过char[]数组的长度。返回-1表示流结束。

    使用char[]数组进行读取:

    try {
        Reader reader = new FileReader("./test.txt");
        char[] buffer = new char[10];
        int n;
        while ((n = reader.read(buffer)) != -1) {   //此时会一次读取10个字符,直到读取完毕时会返回-1
            System.out.println("read " + n + " chars.");
            System.out.println(new String(buffer, 0, n));
        }
        reader.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

    FileReader是默认按中文系统的 GBK 来编码的,所以在读取UTF-8文件时可能会出现乱码情况,因为在 UTF-8 -> GBK -> UTF-8 的过程中编码会出现损失从而造成结果不能还原成最初的字符。

    为了避免读出来是乱码,我们可以在创建FileReader时指定编码。(注意:FileReader 构造函数带编码是在 Java 11 中加入,即JDK11以下的版本不支持。在低版本 JDK 中如果需要指定编码读取文件,我们可以使用 InputStreamReader 来读取文件,该类的构造函数可以指定编码格式)

    public void readFile() throws IOException {
        try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8)) {
            char[] buffer = new char[1000];
            int n;
            while ((n = reader.read(buffer)) != -1) {
                System.out.println("read " + n + " chars.");
            }
        }
    }

    5.2、字节流转成字符流(InputStreamReader,可指定读取时使用的编码格式)

    大部分的字符流 Reader 实际上是基于字节流 InputStream 构造的,因为Reader需要从InputStream中读入字节流(byte),然后,根据编码设置,再转换为char就可以实现字符流。如果我们查看FileReader的源码,它在内部实际上持有一个FileInputStream

    InputStreamReader就可以把任何InputStream转换为ReaderInputStreamReader 是字符流Reader的子类,是字节流通向字符流的桥梁。 该类读取字节,并使用指定的字符集(编码格式)将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。

    你可以在构造器重指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式,例如 GBK 等。

    构造方法如下:

    参数:InputStream in  字节输入流,用来读取文件中保存的字节,String charsetName  指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,...不指定默认使用UTF-8

    InputStreamReader(InputStream in)   //创建一个使用默认字符集的 InputStreamReader。
    InputStreamReader(InputStream in, String charsetName)   //创建使用指定字符集的 InputStreamReader。

    该类继承于 Reader 类,继承了父类的共性成员方法:

    int read()   //读取单个字符并返回。
    int read(char[] cbuf)    //一次读取多个字符,将字符读入数组。
    void close()     //关闭该流并释放与之关联的所有资源。

    使用InputStreamReader读取文件代码示例如下:

    先创建一个InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称。然后使用InputStreamReader对象中的方法read读取文件,最后释放资源。注意:构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码

    public static void main(String[] args) throws IOException {
        //创建一个字节流FileInputStream
        InputStream input = new FileInputStream("./readme.txt");
        //创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称。InputStreamReader将会将字节流按照UTF-8进行解码为字符流
        Reader reader = new InputStreamReader(input, "UTF-8");
    
        //使用InputStreamReader对象中的方法read读取文件
        int len = 0;
        while ((len = reader.read()) != -1) {
            System.out.println((char) len);   //分别打印出读取的单个字符
        }
        // 3.释放资源
        reader.close();
    }

    5.3、按行读取文本文件(BufferedReader)

    BufferedReader 是缓冲字符输入流。它继承于Reader。BufferedReader 的作用是为其他字符输入流添加一些缓冲功能。

    java.io.BufferedReader和java.io.BufferedWriter类各拥有8192字符的缓冲区。当BufferedReader在读取文本文件时,会先尽量从文件中读入字符数据并置入缓冲区,而之后若使用read()方法,会先从缓冲区中进行读取。如果缓冲区数据不足,才会再从文件中读取,使用BufferedWriter时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。

    构造方法:

    BufferedReader br = new BufferReader(Reader in);  //Reader in是输入字符流

    主要方法有:

    int read();//读取单个字符。
    int read(char[] cbuf,int off,int len);  //将字符读入到数组的某一部分。返回读取的字符数。达到尾部 ,返回-1。
    
    String readLine();      //读取文本的一行内容

    BufferedReader 的 readLine() 方法使用起来特别方便,每次读回来的都是一行,避免了需要手动拼接字符串的繁琐

    使用BufferedReader读取文件代码示例:

    import java.io.BufferedReader;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
     
    public class Main 
    {
        public static void main(String[] args) throws IOException{
            //BufferedReader可以按行读取文件
            FileInputStream inputStream = new FileInputStream("d://a.txt");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));  //可以在构造InputStreamReader类时指定使用UTF-8来读取以避免出现乱码情况
                
            String str = null;
            while((str = bufferedReader.readLine()) != null){
                System.out.println(str);
            }
                
            //close
            inputStream.close();
            bufferedReader.close();
        }
    }

    6、输出字符流(Writer)

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

    Writer是所有字符输出流的超类,它提供的方法主要有:

    • 写入一个字符(0~65535):void write(int c)
    • 写入字符数组的所有字符:void write(char[] c)
    • 写入String表示的所有字符:void write(String s)

    6.1、文件输出字符流(FileWriter)

    FileWriter就是向文件中写入字符流的Writer。它的使用方法和FileReader类似:

    try {
        Writer writer = new FileWriter("./readme.txt");
        writer.write('H'); // 写入单个字符
        writer.write("Hello".toCharArray()); // 写入char[]
        writer.write("你好啊hello~"); // 直接写入String
    
        writer.flush();  //可不写
        writer.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

    6.2、字节流转成字符流(OutputStreamWriter,可指定输出时的编码)

    大部分的 Writer 实际上是基于OutputStream构造的,它接收char,然后在内部自动转换成一个或多个byte,并写入OutputStreamOutputStreamWriter就是一个将任意的OutputStream转换为Writer的转换器。

    构造方法:参数:OutputStream out:字节输出流,可以用来写转换之后的字节到文件中,String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,...不指定将默认使用UTF-8

    OutputStreamWriter(OutputStream out)   //创建使用默认字符编码的 OutputStreamWriter。
    OutputStreamWriter(OutputStream out, String charsetName)  //创建使用指定字符集的 OutputStreamWriter。

    该类继承 Writer 类,继承了父类的共性成员方法

    void write(int c)        //写入单个字符。
    void write(char[] cbuf)  //写入字符数组。
    abstract  void write(char[] cbuf, int off, int len)  //写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
    void write(String str)  //写入字符串。
    void write(String str, int off, int len)   //写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
    void flush()   //刷新该流的缓冲。
    void close()   //关闭此流,但要先刷新它。 

    使用 OutputStreamWriter 输出字符代码示例:

    public static void main(String[] args) throws IOException {
        //1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\utf_8.txt"),"utf-8");   //可指定编码格式
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\utf_8.txt"));//不指定将默认使用UTF-8
    //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码) osw.write("你好");
    //3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程) osw.flush(); //4.释放资源 osw.close(); }

    6.3、BufferedWriter

    BufferedReader和BufferedWriter都是带有默认缓冲区的字符输入输出流,其效率相较于没有缓冲区要高。使用BufferedWriter时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。

    BufferedWriter通过字符数组来缓冲数据,当缓冲区满或者用户调用flush()函数时,它就会将缓冲区的数据写入到输出流中。

    构造方法:

    bufferedWriter bf = new bufferedWriter(Writer out );  //Writer out是输出的字符流

    主要方法:

    void write(char ch);//写入单个字符。
    void write(char []cbuf,int off,int len) //写入字符数据的某一部分。
    void write(String s,int off,int len)    /写入字符串的某一部分。
    void newLine()  //写入一个行分隔符。
    void flush();   //刷新该流中的缓冲。将缓冲数据写到目的文件中去。
    void close();   //关闭此流,再关闭前会先刷新他。

    使用BufferWriter往文件输出内容代码示例:

    public static void outMsg(String msg, boolean flag, String filename) {
        try {
            File file = new File(filename);
            OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(file, flag), "UTF-8");  //可以在声明FileOutputStreamWriter时指定编码
            BufferedWriter writer = new BufferedWriter(write);  //声明BufferedWriter时需传入字符输入流
            writer.write(msg);
            writer.flush();
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  • 相关阅读:
    水晶报表常见问题
    winform post xml 并获取返回xml
    C#操作INI文件
    将Stream转换成String,将String转化成Stream
    winform异步监听POST请求
    winform异步获取POST过来的XML
    C#读取XML C#写入XML
    XDocument转XmlDocument ,XmlDocument转XDocument 静态扩展方法
    C#读取XML节点
    Java 字符转码之UTF8转为GBK/GB2312
  • 原文地址:https://www.cnblogs.com/wenxuehai/p/12859217.html
Copyright © 2020-2023  润新知