Java I/O流 03

I/O流·字符流

字符流FileReader

* A:字符流是什么
  * 字符流是可以直接读写字符的 IO流
  * 字符流读取字符,就要先读取到字节数据,然后转换为字符;如果要写出字符,需要把字符转换为字节再写出

* B:FileReader
  * FileReader类的read() 方法可以按照字符大小读取

package com.heima.chario;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Demo1_FileReader {

    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();
    }

    public static void demo2() throws FileNotFoundException, IOException {
        FileReader fr = new FileReader("xxx.txt");
        int c; // 默认返回的是int类型
        
        while ((c = fr.read()) != -1) {    // 通过项目默认的码表,一次读取一个字符
            System.out.println((char) c); // 强转为char类型
        }
        fr.close();
    }

    public static void demo1() throws FileNotFoundException, IOException {
        FileReader fr = new FileReader("xxx.txt"); // 创建字符输入流
        char c = (char) fr.read(); // 读取一个字符并强转为char类型
        
        System.out.println(c);
        fr.close(); // 关流
    }
}
FileReader

 

字符流FileWriter

* A:FileWriter类的 write()方法可以自动把字符转为字节写出

package com.heima.chario;

import java.io.FileWriter;

public class Demo2_FileWriter {

    public static void main(String[] args) throws Exception {
        FileWriter fw = new FileWriter("yyy.txt"); // 创建字符输出流
        fw.write("大家好,基础班快接近尾声了,大家要努力啊!!"); // 写入字符串,底层通过编码表翻译后用字节写入
        fw.write(97);
        fw.close(); // 关流
    }
}
FileWriter

 

什么情况下使用字符流

* 字符流也可以拷贝文本文件,但不推荐是同,因为读取时会把字节转为字符,写入时会把字符转为字节
* 程序如果只需要读取一段文本或者只需要写出一段文本的时候,可以使用字符流
  * 因为读取的时候是按照字符的大小读取的,不会出现半个中文的情况
  * 写入的是红可以直接将字符串写出,不用转换为字节

* 字符流不可以拷贝非纯文本文件
  * 因为再读的时候会将字节转换为字符,在转换的过程中可能找不到对应的字符,就会用?代替
  * 如果读取为?,写出时也是?的字节,这样文件的乱码了

 

用字符流拷贝文件

 * A:普通字符流拷贝

* B:带缓冲的字符流
  * BufferedReader的 read()方法读取字符时会一次读取若干个字符到缓冲区,然后逐个返回给程序,降低读取文件的次数,提高效率
  * BufferedWriter的 write()方法写出字符时会先写到缓冲区,缓冲区写满后才会写到文件,降低写文件的次数,提高效率

package com.heima.chario;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo3_Copy {

    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();
        // demo3();
        // demo4();
    }

    public static void demo4() throws FileNotFoundException, IOException {
        BufferedReader br = new BufferedReader(new FileReader("xxx.txt")); // 创建带缓冲的字符输入流,关联普通字符输入流
        BufferedWriter bw = new BufferedWriter(new FileWriter("yyy.txt"));
        
        int c;
        while ((c = br.read())!= -1) {
            bw.write(c);
        }
        
        br.close();
        bw.close();
    }

    public static void demo3() throws FileNotFoundException, IOException {
        FileReader fr = new FileReader("xxx.txt"); // 创建字符输入流
        FileWriter fw = new FileWriter("yyy.txt"); // 创建字符输出流

        char[] arr = new char[1024 * 8]; // 创建字符数组接收数据
        int len;
        while ((len = fr.read(arr)) != -1) { // 将文件上的数据读取到字符数组中
            fw.write(arr, 0, len);    // 将字符数组中的数据写到文件上
        }
        fr.close();
        fw.close();
    }

    public static void demo2() throws FileNotFoundException, IOException {
        // 字符流不能拷贝非纯文本文件
        FileReader fr = new FileReader("亲爱的小孩.mp3");
        FileWriter fw = new FileWriter("copy1.mp3");
        
        int c;
        while ((c = fr.read()) != -1) {
            fw.write(c);
        }
        fr.close();
        fw.close();
    }

    public static void demo1() throws FileNotFoundException, IOException {
        FileReader fr = new FileReader("xxx.txt"); // 创建字符输入流
        FileWriter fw = new FileWriter("zzz.txt"); // 创建字符输出流

        int c; // 接收每一个字符
        while ((c = fr.read()) != -1) {
            fw.write(c); // 将读取到的内容写入文件
        }
        
        fr.close(); // 关流
        fw.close(); // Writer类中有一个2k的小缓冲区,程序会将内容写到缓冲区内,关流会将缓冲区内的内容刷新到硬盘上
    }
}
Copy

 

readLine() 和 newLine() 方法

* BufferedReader的 readLine()方法可以读取一行字符(不包含换行符)

* BufferedWriter的 newLine()方法可以输出一个跨平台的换行符

package com.heima.chario;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo4_Buffered {
    /*
     * 带缓冲区的流中的特殊方法:
     * readLine():逐行读取
     * newLine():写出回车换行符
     */
    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();
    }

    public static void demo2() throws FileNotFoundException, IOException {
        BufferedReader br = new BufferedReader(new FileReader("zzz.txt")); // 创建带缓冲的字符流
        BufferedWriter bw = new BufferedWriter(new FileWriter("aaa.txt"));

        String lineString;
        while ((lineString = br.readLine()) != null) {
            bw.write(lineString);
            //bw.write("\r\n");
            bw.newLine();    // newLine 和 \r\n 的区别是:前者是跨平台的,而后者仅在windows代表换行
        }
        
        br.close(); // 关流
        bw.close();
    }

    public static void demo1() throws FileNotFoundException, IOException {
        BufferedReader br = new BufferedReader(new FileReader("zzz.txt")); // 创建带缓冲的字符流
        String lineString;

        while ((lineString = br.readLine()) != null) { // 逐行读取
            System.out.println(lineString);
        }
        br.close(); // 关流
    }
}
ReadLine

 

将文本反转

* 将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换

package com.heima.test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

public class Test1 {
    /*
     * 分析: 
     * 1、创建输入输出流对象 
     * 2、创建集合对象,把数据进行临时存储 
     * 3、将读到的数据存储在集合中
     * 4、倒着遍历集合,将数据写到文件上 
     * 5、关流
     * 
     * 注意事项:流对象尽量晚开早关
     */
    public static void main(String[] args) throws IOException {
        ArrayList<String> list = new ArrayList<String>(); // 创建空列表
        String lineString;
        BufferedReader br = new BufferedReader(new FileReader("zzz.txt")); // 创建带缓冲的字符流
        
        while ((lineString = br.readLine()) != null) { // 从文件中逐行读取
            list.add(lineString); // 逐行存入列表中
        }
        br.close();    // 关流1
        
        //Collections.reverse(list); // 也可以调用集合工具类先反转,再正着遍历
        BufferedWriter bw = new BufferedWriter(new FileWriter("revzzz.txt"));
        for (int i = list.size() - 1; i >= 0; i--) { // 倒着遍历列表
            bw.write(list.get(i)); // 通过索引获取列表元素并写入文件中
            bw.newLine(); // 换行
        }
        bw.close(); // 关流2
    }
}
Test1

 

LineNumberReader

* LineNumberReader是BufferedReader的子类,具有相同的功能,并且可以统计行号
  * 调用 getLineNumber()方法可以获取当前行号
  * 调用 setLineNumber()方法可以设置当前行号

package com.heima.chario;

import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;

public class Demo5_LineNumberReader {

    public static void main(String[] args) throws IOException {
        LineNumberReader lnr = new LineNumberReader(new FileReader("zzz.txt"));
        
        String lineString;
        lnr.setLineNumber(100);    // 设置初始的行号,默认为0,每readLine一次,行号就加一
        while ((lineString = lnr.readLine())!=null) {
            System.out.println(lnr.getLineNumber() + ":" + lineString);
        }
        lnr.close();
    }
}
LineNumberReader

 

装饰设计模式

package com.heima.chario;

public class Demo6_Wrap {
    /*
     * 装饰设计模式的好处是:
     * 耦合性低,被装饰类的变化与装饰类的变化无关
     */
    public static void main(String[] args) {
        HeiMaStudent hms = new HeiMaStudent(new Student());
        hms.code();
    }
}

interface Coder {
    public void code(); // 定义抽象方法
}

class Student implements Coder {

    @Override
    public void code() {
        System.out.println("javase");
        System.out.println("javaweb");
    }

}

class HeiMaStudent implements Coder { // 目标是装饰Student类,对其进行强化
    // 获取被装饰类的引用
    private Student student; // 获取学生引用
    
    // 在构造方法中传入被装饰类的对象
    public HeiMaStudent(Student s) {
        this.student = s;
    }

    // 对原有的功能进行升级
    @Override
    public void code() {
        student.code();
        System.out.println("ssh");
        System.out.println("数据库");
        System.out.println("hadoop");
        System.out.println("...");
    }

}
Wrap

 

使用指定的码表读写字符

* FileReader是使用默认码表读取文件,如果需要使用指定码表读取,那么可以使用InputStreamReader(字节流,编码表)

* FileWriter是使用默认码表写文件,如果需要使用指定码表写出,那么可以使用OutputStreamReader(字节流,编码表)

package com.heima.chario;

import java.io.*;


public class Demo7_TransIO {

    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();
        // demo3();
    }

    public static void demo3() throws UnsupportedEncodingException, FileNotFoundException, IOException {
        // 采用带缓冲的字符流,进行更高效的读写,装饰设计模式的思想
        BufferedReader br = new BufferedReader
                (new InputStreamReader(new FileInputStream("utf-8.txt"), "utf-8")); // 创建带缓冲的字符流,关联转换流
        BufferedWriter bw = new BufferedWriter
                (new OutputStreamWriter(new FileOutputStream("gbk.txt"), "gbk"));
        
        int len;
        while ((len = br.read())!= -1) { // 使用内置的缓冲区读取数据
            bw.write(len);
        }
        
        br.close(); // 关流
        bw.close();
    }

    public static void demo2() throws UnsupportedEncodingException, FileNotFoundException, IOException {
        // 指定码表读和写字符
        InputStreamReader isr = new InputStreamReader(new FileInputStream("utf-8.txt"),"utf-8"); // 创建字符转换流,关联字节流
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk"); // 编码形式可以省略,省略默认为项目的编码表
        
        int c;
        while ((c = isr.read())!= -1) { // 逐个字符读取
            osw.write(c);
        }
        
        isr.close();
        osw.close();
    }

    public static void demo1() throws FileNotFoundException, IOException {
        // 用默认编码表读写,出现乱码
        FileReader fr = new FileReader("utf-8.txt"); // 内容由utf-8编码
        FileWriter fw = new FileWriter("gbk.txt"); // 内容由gbk编码

        int c;
        while ((c = fr.read()) != -1) {
            fw.write(c);
        }

        fr.close();
        fw.close();
    }
}
InputStreamReader

 

获取文本上字符出现的次数

* 获取文本上每个字符出现的次数,将结果写在 times.txt上

package com.heima.test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.TreeMap;

public class Test2 {
    /*
     * 分析:
     * 1、创建带缓冲的输入流对象
     * 2、创建双列集合对象TreeMap
     * 3、将读到的字符存储在双列集合中,存储的时候要做判断,如果不包含这个键,就将键和1存储,如果包含这个键,就将键和值+1存储
     * 4、关闭输入流,创建输出流对象
     * 5、遍历集合,将集合的内容写到times.txt中
     * 6、关闭输出流
     */
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("zzz.txt")); //创建带缓冲的输入流对象
        TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>(); // 创建双列集合
        
        int c;
        while ((c = br.read())!= -1) { // 从文件中逐个读取字符
            char ch = (char)c;    // 向下强转
            /*if (!tm.containsKey(ch)) {
                tm.put(ch, 1);
            }else {
                tm.put(ch, tm.get(ch)+1);
            }*/
            tm.put(ch, !tm.containsKey(ch)? 1 : tm.get(ch)+1); // 将字符及其出现次数存储到双列集合中
        }
        br.close(); // 关闭输入流
        
        BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt")); // 创建输出流对象
        for (Character key : tm.keySet()) { // 遍历键的集合,获取每个键
            /*if (key == '\r') {
                bw.write("\\r" + "=" + tm.get(key));
                bw.newLine();
            } else if (key == '\n') {
                bw.write("\\n" + "=" + tm.get(key));
                bw.newLine();
            }else if (key == '\t') {
                bw.write("\\t" + "=" + tm.get(key));
                bw.newLine();
            }else {
                bw.write(key + "=" + tm.get(key));
                bw.newLine();                
            }*/
            
            switch (key) { // 将集合中的内容写到文件中
            case '\t': {
                bw.write("\\t" + "=" + tm.get(key)); 
                break;
            }
            case '\r': {
                bw.write("\\r" + "=" + tm.get(key));
                break;
            }
            case '\n': {
                bw.write("\\n" + "=" + tm.get(key));
                break;
            }
            
            default:
                bw.write(key + "=" + tm.get(key));
            }
            bw.newLine(); // 输入换行
        }
        
        bw.close(); // 关闭输出流
    }
}
Test2

 

 

试用版软件

* 当我们下载一个试用版软件时,每执行一次就会提醒我们还有多少次使用的机会
* 用IO知识,模拟试用版软件,试用10次,每执行一次, 提示一次还剩几次机会,如果次数到了,就提示:请购买正版

package com.heima.test;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Test3 {
    /*
     * 分析:
     * 1、创建待缓冲的输入流对象,使用readLine()方法,保证数据的原样性
     * 2、将读到的字符串转换为int数
     * 3、对int数进行判断,如果大于0,就将其减一,并写回源文件;如果小于等于0,就提示"请购买正版软件"
     * 
     */
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("config.txt"));
        String line = br.readLine(); // 整行读取
        int c = Integer.parseInt(line); // 将字符串型数字转换为int型数字
        br.close(); // 关闭输入流

        FileWriter fw = new FileWriter("config.txt"); // 会清空config.txt文件
        if (c > 0) {
            System.out.println("您还有 " + c-- + "次机会");
            fw.write(c + ""); // 打印字符串
        } else {
            System.out.println("试用次数以耗尽,请购买正版");
            fw.write(0 + ""); // 将0写回文件,否则config.txt就是空的了
        }
        
        fw.close(); // 关闭输出流,如果忘记关流,数据在缓冲区内不会被刷过去
        
    }
}
Test3

 

 

递归

* 递归的弊端:不能调用太多次,容易导致栈溢出
* 递归的好处:不用知道循环次数

* 练习:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的 .java文件

package com.heima.test;

import java.io.File;
import java.util.Scanner;

public class Test4 {
    /*
     * 分析:
     * 1、从键盘接收一个文件夹路径
     *         如果录入的是不存在的,给提示
     *         如果录入的是文件路径,也给提示
     *         如果是文件夹路径,直接返回
     * 
     * 2、获取到该文件夹下的所有文件和文件夹,存储在一个file数组中
     * 
     * 3、遍历数组,对每一个文件或文件夹做判断,
     *         如果是文件,并且后缀是.java就打印;
     *         如果是文件夹,就递归调用
     *
     * 注意:构造方法不能递归调用
     */
    
    public static void main(String[] args) {
        File dir = getDir(); // 获取文件夹路径
        printJavaFile(dir); // 调用递归方法
        
    }
    
    /*
     * 获取键盘录入的文件夹路径
     * 1、返回值类型,File
     * 2、不需要参数
     */
    public static File getDir() {
        Scanner sc = new Scanner(System.in); // 创建键盘录入对象
        System.out.println("请输入一个文件夹路径"); // 提示输入
        while (true) { // 定义无限循环
            String liString = sc.nextLine(); // 将键盘录入的文件夹路径存储
            File dir = new File(liString); // 将录入的内容封装成File对象
            if (!dir.exists()) { // 对File对象进行一系列判断
                System.out.println("您录入的文件夹路径不存在,请重新录入");
            } else if (dir.isFile()) {
                System.out.println("您录入的是文件路径,请您录入文件夹路径");
            } else {
                sc.close();
                return dir; // 返回File对象
            }
        }
    }
    /*
     * 获取文件夹路径下的所有.java文件
     * 1、返回值为void
     * 2、参数列表为File dir
     */
    public static void printJavaFile(File dir) { // 传入文件夹路径
        File[] subFiles = dir.listFiles(); // 获取该路径下所有的文件和文件夹
        for (File subfile : subFiles) { // 遍历File数组
            if (subfile.isFile() && subfile.getName().endsWith(".java")) { // 判断是否符合条件
                System.out.println(subfile.getName()); // 如果符合,就打印
            } else if (subfile.isDirectory()) { // 如果是文件夹
                printJavaFile(subfile); // 递归调用
            }
        }
    }
}
Test4

 

posted @ 2020-04-16 13:35  小么VinVin  阅读(143)  评论(0编辑  收藏  举报